optimize asset merging

This commit is contained in:
Evan You 2015-05-27 12:08:17 -04:00
parent c0d22e8cae
commit a316dfe9a4
15 changed files with 78 additions and 84 deletions

View File

@ -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"
]

View File

@ -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]

View File

@ -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({

View File

@ -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'
]
}

View File

@ -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(

View File

@ -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')

View File

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

View File

@ -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) {

View File

@ -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'))

View File

@ -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

View File

@ -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
)
}

View File

@ -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 () {

View File

@ -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',

View File

@ -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')
})

View File

@ -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' }
}