فهرست منبع

optimize asset merging

Evan You 11 سال پیش
والد
کامیت
a316dfe9a4

+ 1 - 1
component.json

@@ -69,8 +69,8 @@
     "src/util/env.js",
     "src/util/index.js",
     "src/util/lang.js",
-    "src/util/merge-option.js",
     "src/util/misc.js",
+    "src/util/options.js",
     "src/vue.js",
     "src/watcher.js"
   ]

+ 3 - 10
src/api/global.js

@@ -1,5 +1,5 @@
 var _ = require('../util')
-var mergeOptions = require('../util/merge-option')
+var config = require('../config')
 
 /**
  * Expose useful internals
@@ -48,7 +48,7 @@ exports.extend = function (extendOptions) {
   Sub.prototype = Object.create(Super.prototype)
   Sub.prototype.constructor = Sub
   Sub.cid = cid++
-  Sub.options = mergeOptions(
+  Sub.options = _.mergeOptions(
     Super.options,
     extendOptions
   )
@@ -101,13 +101,6 @@ exports.use = function (plugin) {
  * @param {Function} Constructor
  */
 
-var assetTypes = [
-  'directive',
-  'elementDirective',
-  'filter',
-  'transition'
-]
-
 function createAssetRegisters (Constructor) {
 
   /* Asset registration methods share the same signature:
@@ -116,7 +109,7 @@ function createAssetRegisters (Constructor) {
    * @param {*} definition
    */
 
-  assetTypes.forEach(function (type) {
+  config._assetTypes.forEach(function (type) {
     Constructor[type] = function (id, definition) {
       if (!definition) {
         return this.options[type + 's'][id]

+ 6 - 3
src/compiler/compile.js

@@ -3,6 +3,7 @@ var config = require('../config')
 var textParser = require('../parsers/text')
 var dirParser = require('../parsers/directive')
 var templateParser = require('../parsers/template')
+var resolveAsset = _.resolveAsset
 
 // internal directives
 var propDef = require('../directives/prop')
@@ -278,7 +279,7 @@ function processTextToken (token, options) {
   }
   function setTokenType (type) {
     token.type = type
-    token.def = options.directives[type]
+    token.def = resolveAsset(options, 'directives', type)
     token.descriptor = dirParser.parse(token.value)[0]
   }
   return el
@@ -469,7 +470,7 @@ function makePropsLinkFn (props) {
 
 function checkElementDirectives (el, options) {
   var tag = el.tagName.toLowerCase()
-  var def = options.elementDirectives[tag]
+  var def = resolveAsset(options, 'elementDirectives', tag)
   if (def) {
     return makeTerminalNodeLinkFn(el, tag, '', options, def)
   }
@@ -539,6 +540,8 @@ skip.terminal = true
 
 function makeTerminalNodeLinkFn (el, dirName, value, options, def) {
   var descriptor = dirParser.parse(value)[0]
+  // no need to call resolveAsset since terminal directives
+  // are always internal
   def = def || options.directives[dirName]
   var fn = function terminalNodeLinkFn (vm, el, host) {
     vm._bindDir(dirName, el, descriptor, def, host)
@@ -571,7 +574,7 @@ function compileDirectives (elOrAttrs, options) {
     if (value === null) continue
     if (name.indexOf(config.prefix) === 0) {
       dirName = name.slice(config.prefix.length)
-      dirDef = options.directives[dirName]
+      dirDef = resolveAsset(options, 'directives', dirName)
       _.assertAsset(dirDef, 'directive', dirName)
       if (dirDef) {
         dirs.push({

+ 14 - 1
src/config.js

@@ -62,7 +62,20 @@ module.exports = {
    * @type {Boolean}
    */
 
-  _delimitersChanged: true
+  _delimitersChanged: true,
+
+  /**
+   * List of asset types that a component can own.
+   *
+   * @type {Array}
+   */
+
+  _assetTypes: [
+    'directive',
+    'elementDirective',
+    'filter',
+    'transition'
+  ]
 
 }
 

+ 2 - 3
src/directives/repeat.js

@@ -6,7 +6,6 @@ var expParser = require('../parsers/expression')
 var templateParser = require('../parsers/template')
 var compile = require('../compiler/compile')
 var transclude = require('../compiler/transclude')
-var mergeOptions = require('../util/merge-option')
 var uid = 0
 
 // async component resolution states
@@ -133,7 +132,7 @@ module.exports = {
         return
       }
       this.Ctor = Ctor
-      var merged = mergeOptions(Ctor.options, {}, {
+      var merged = _.mergeOptions(Ctor.options, {}, {
         $parent: this.vm
       })
       merged.template = this.inlineTempalte || merged.template
@@ -175,7 +174,7 @@ module.exports = {
       _.define(context, key, meta[key])
     }
     var id = this.ctorGetter.call(context, context)
-    var Ctor = this.vm.$options.components[id]
+    var Ctor = _.resolveAsset(this.vm.$options, 'components', id)
     _.assertAsset(Ctor, 'component', id)
     if (!Ctor.options) {
       _.warn(

+ 1 - 1
src/directives/transition.js

@@ -19,7 +19,7 @@ module.exports = {
       // so the transition module knows this is a
       // javascript transition without having to check
       // computed CSS.
-      fns: vm.$options.transitions[id]
+      fns: _.resolveAsset(vm.$options, 'transitions', id)
     }
     if (oldId) {
       _.removeClass(this.el, oldId + '-transition')

+ 1 - 1
src/instance/init.js

@@ -1,4 +1,4 @@
-var mergeOptions = require('../util/merge-option')
+var mergeOptions = require('../util').mergeOptions
 
 /**
  * The main init sequence. This is called for every

+ 2 - 4
src/instance/misc.js

@@ -11,8 +11,7 @@ var _ = require('../util')
  */
 
 exports._applyFilter = function (id, args) {
-  var registry = this.$options.filters
-  var filter = registry[id]
+  var filter = _.resolveAsset(this.$options, 'filters', id)
   _.assertAsset(filter, 'filter', id)
   return (filter.read || filter).apply(this, args)
 }
@@ -29,8 +28,7 @@ exports._applyFilter = function (id, args) {
  */
 
 exports._resolveComponent = function (id, cb) {
-  var registry = this.$options.components
-  var factory = registry[id]
+  var factory = _.resolveAsset(this.$options, 'components', id)
   _.assertAsset(factory, 'component', id)
   // async component factory
   if (!factory.options) {

+ 2 - 1
src/util/index.js

@@ -5,4 +5,5 @@ extend(exports, lang)
 extend(exports, require('./env'))
 extend(exports, require('./dom'))
 extend(exports, require('./misc'))
-extend(exports, require('./debug'))
+extend(exports, require('./debug'))
+extend(exports, require('./options'))

+ 7 - 3
src/util/misc.js

@@ -1,5 +1,6 @@
-var _ = require('./debug')
+var _ = require('./index')
 var config = require('../config')
+var commonTagRE = /^(div|p|span|img|a|br|ul|ol|li|h1|h2|h3|h4|h5|table|tbody|tr|td|pre)$/
 
 /**
  * Check if an element is a component, if yes return its
@@ -17,7 +18,10 @@ exports.checkComponent = function (el, options) {
     var exp = el.getAttribute('is')
     el.removeAttribute('is')
     return exp
-  } else if (options.components[tag]) {
+  } else if (
+    !commonTagRE.test(tag) &&
+    _.resolveAsset(options, 'components', tag)
+  ) {
     return tag
   }
 }
@@ -66,7 +70,7 @@ exports.resolveFilters = function (vm, filters, target) {
   var res = target || {}
   // var registry = vm.$options.filters
   filters.forEach(function (f) {
-    var def = vm.$options.filters[f.name]
+    var def = _.resolveAsset(vm.$options, 'filters', f.name)
     _.assertAsset(def, 'filter', f.name)
     if (!def) return
     var args = f.args

+ 29 - 22
src/util/merge-option.js → src/util/options.js

@@ -135,23 +135,11 @@ strats.directives =
 strats.filters =
 strats.transitions =
 strats.components =
-strats.elementDirectives = function (parentVal, childVal, vm, key) {
-  var ret = Object.create(
-    vm && vm.$parent
-      ? vm.$parent.$options[key]
-      : _.Vue.options[key]
-  )
-  if (parentVal) {
-    var keys = Object.keys(parentVal)
-    var i = keys.length
-    var field
-    while (i--) {
-      field = keys[i]
-      ret[field] = parentVal[field]
-    }
-  }
-  if (childVal) extend(ret, childVal)
-  return ret
+strats.elementDirectives = function (parentVal, childVal) {
+  var res = Object.create(parentVal)
+  return childVal
+    ? extend(res, childVal)
+    : res
 }
 
 /**
@@ -233,26 +221,45 @@ function guardComponents (components) {
  *                     an instantiation merge.
  */
 
-module.exports = function mergeOptions (parent, child, vm) {
+exports.mergeOptions = function merge (parent, child, vm) {
   guardComponents(child.components)
   var options = {}
   var key
   if (child.mixins) {
     for (var i = 0, l = child.mixins.length; i < l; i++) {
-      parent = mergeOptions(parent, child.mixins[i], vm)
+      parent = merge(parent, child.mixins[i], vm)
     }
   }
   for (key in parent) {
-    merge(key)
+    mergeField(key)
   }
   for (key in child) {
     if (!(parent.hasOwnProperty(key))) {
-      merge(key)
+      mergeField(key)
     }
   }
-  function merge (key) {
+  function mergeField (key) {
     var strat = strats[key] || defaultStrat
     options[key] = strat(parent[key], child[key], vm, key)
   }
   return options
+}
+
+/**
+ * Resolve an asset.
+ * This function is used because child instances need access
+ * to assets defined in its ancestor chain.
+ *
+ * @param {Object} options
+ * @param {String} type
+ * @param {String} id
+ * @return {Object|Function}
+ */
+
+exports.resolveAsset = function resolve (options, type, id) {
+  return options[type][id] || (
+    options._parent
+      ? resolve(options._parent.$options, type, id)
+      : null
+  )
 }

+ 2 - 1
test/unit/specs/api/child_spec.js

@@ -1,4 +1,5 @@
 var Vue = require('../../../../src/vue')
+var _ = Vue.util
 
 describe('Child API', function () {
 
@@ -22,7 +23,7 @@ describe('Child API', function () {
     expect(child.$parent).toBe(vm)
     expect(child.$root).toBe(vm)
     expect(vm._children.indexOf(child)).toBe(0)
-    expect(child.$options.directives.test).toBeTruthy()
+    expect(_.resolveAsset(child.$options, 'directives', 'test')).toBeTruthy()
   })
 
   it('inherit scope', function () {

+ 3 - 4
test/unit/specs/compiler/compile_spec.js

@@ -1,7 +1,6 @@
 var Vue = require('../../../../src/vue')
 var _ = require('../../../../src/util')
 var dirParser = require('../../../../src/parsers/directive')
-var merge = require('../../../../src/util/merge-option')
 var compile = require('../../../../src/compiler/compile')
 var transclude = require('../../../../src/compiler/transclude')
 
@@ -50,7 +49,7 @@ if (_.inBrowser) {
       var defB = { priority: 2 }
       var descriptorA = dirParser.parse('a')[0]
       var descriptorB = dirParser.parse('b')[0]
-      var options = merge(Vue.options, {
+      var options = _.mergeOptions(Vue.options, {
         directives: {
           a: defA,
           b: defB
@@ -120,7 +119,7 @@ if (_.inBrowser) {
     })
 
     it('custom element components', function () {
-      var options = merge(Vue.options, {
+      var options = _.mergeOptions(Vue.options, {
         components: {
           'my-component': {}
         }
@@ -146,7 +145,7 @@ if (_.inBrowser) {
     })
 
     it('props', function () {
-      var options = merge(Vue.options, {
+      var options = _.mergeOptions(Vue.options, {
         _asComponent: true,
         props: [
           'a',

+ 1 - 1
test/unit/specs/directives/transition_spec.js

@@ -26,7 +26,7 @@ if (_.inBrowser) {
       expect(dir.el.className === 'test-transition')
       dir.update('lol', 'test')
       expect(dir.el.__v_trans.id).toBe('lol')
-      expect(dir.el.__v_trans.fns).toBeUndefined()
+      expect(dir.el.__v_trans.fns).toBeNull()
       expect(dir.el.className === 'lol-transition')
     })
 

+ 4 - 28
test/unit/specs/util/merge-option_spec.js → test/unit/specs/util/options_spec.js

@@ -1,6 +1,6 @@
 var _ = require('../../../../src/util')
 var Vue = require('../../../../src/vue')
-var merge = require('../../../../src/util/merge-option')
+var merge = _.mergeOptions
 
 describe('Util - Option merging', function () {
   
@@ -101,42 +101,18 @@ describe('Util - Option merging', function () {
   it('assets', function () {
     var asset1 = {}
     var asset2 = {}
-    var asset3 = {}
-    var asset4 = {}
-    var asset5 = {}
     var res = merge(
       { directives: { a: asset1 }},
       { directives: { b: asset2 }}
     ).directives
     expect(res.a).toBe(asset1)
     expect(res.b).toBe(asset2)
-    // vm asset merge should do tree-way merge
-    var proto = { d: asset5 }
-    var parent = { directives: Object.create(proto) }
-    parent.directives.a = asset1
-    parent.directives.b = asset4
-    res = merge(
-      parent,
-      { directives: { b: asset2 }},
-      {
-        $parent: {
-          $options: {
-            directives: { c: asset3 }
-          }
-        }
-      },
-      'directives'
-    ).directives
-    expect(res.a).toBe(asset1)
-    // child should overwrite parent
-    expect(res.b).toBe(asset2)
-    expect(res.c).toBe(asset3)
-    // should not copy parent prototype properties
-    expect(res.d).toBeUndefined()
   })
 
   it('guard components', function () {
-    var res = merge({}, {
+    var res = merge({
+      components: null
+    }, {
       components: {
         a: { template: 'hi' }
       }