mergeField
根据不同的配置项使用不同的方法合并配置,
mergeOptions
代码片段
var options = {}
var key
for(key in parent) {mergeField(key)}
for(key in child) {mergeField(key)}
function mergeField(key) {
var strat = strats[key] || defaultStrat
options[key] = strat(parent[key],child[key],vm)
}
strats 对象
starts 对象预制了一些方法用来处理一些固定的配置项
el 、propsData
这2个选项将使用默认合并方法defaultStrat
合并数据, 但是它们只有在创建实例的时候才有效, 如果不是在实例时合并,就会提示错误,但还是会合并
strats.el = strats.propsData = function(parent,child,vm,key){
if(!vm) {...}
return defaultStrat(parent,child)
}
data
合并data配置项,parentV就是 Vue.options.data , childV就是传入的data , 接着执行 mergeDataOrFn
函数
strats.data = function(parentV,childV,vm) {
if(!vm) {
if(childV && typeof childV != "function") {
return parentV
}
return mergeDataOrFn(parentV,childV)
}
return mergeDataOrFn(parentV,childV,vm)
}
mergeDataOrFn 分为2种合并,1. extend、mixin
合并和 2. 实例化时合并
extend、mixin
合并: 只要一侧传入的是undefined,就没有合并的必要,直接返回另一侧data函数
. 当两个参数都有值时,返回一个闭包函数mergeDataFn
,它的参数就是各自的data数据
function mergeDataOrFn(parentV,childV,vm) {
if(!vm) {
if(!childV) {
return parentV
}
if(!parentV) {
return childV
}
return function mergeDataFn() {
return mergeData(
typeof childV === "function" ? childV.call(this,this) : childV,
typeof parentV === "function" ? parentV.call(this,this) : parentV
)
}
} else {...}
}
- 实例化时传入了vm参数,则返回一个
mergedInstanceDataFn
闭包函数, 其实细看的话,和mergeDataFn差不多
if(!vm){...} else {
return function mergedInstanceDataFn() {
var instanceData = typeof childVal === 'function' ? childVal(vm,vm) : childVal
var defaultData = typeof parentVal === 'function' ? parentVal(vm,vm) : parentVal
if(instanceData) {
return mergeData(instanceData,defaultData)
} else {
return defaultData
}
}
}
props、methods、inject、computed
这几个属性公用一个函数,功能是一致的.就是将 Vue.options上的和传入的配置项合并到一个空的对象中并返回 . 完整代码:
strats.props =
strats.methods =
strats.inject =
strats.computed = function (parentV,childV,vm,key) {
if(childV && "development" != "production") {
assertObjectType(key,childV,vm)
}
if(!parentV) {return childV}
var ret = Object.create(null)
extend(ret,parentV)
if(childV){extend(ret,childV)}
return ret
}
assertObjectType
就是对配置项验证,格式必须为 [object Object]
如果 Vue.options 上没有对应的配置,则无需合并,直接返回传入的配置
if(!parentV) {return childV}
用一个空的对象来合并childV和parentV, 依次遍历属性添加到对象中
var ret = Object.create(null)
extend(ret,parentV)
if(childV) {extend(ret,childV)}
return ret
extend(to,from)
它的功能就是遍历对象,将from里的属性挨个添加到 to 上面
function extend(to,from) {
for(var key in from) {
to[key] = from[key]
}
return to
}
watch
先看看合并后的结果,如果两边都有数据,则合并后的watch是一个数组
watch: {
test: [fn,fn]
}
- 用户没有传入 watch选项时, 把Vue.options上的 watch 作为原型新建一个对象返回
- Vue.options 上没有 watch选项时, 原样返回用户传入的watch配置
- 两边都有时,先遍历Vue.options的watch把属性添加到临时变量ret上(又见
extend函数
,上面了解过它就是遍历对象把属性添加到另一个对象上,在这里也就是ret对象
),再遍历用户传入的配置
stras.watch = function (parentV,childV,vm,key) {
if(!childV) {return Object.create(parentV || null)}
if(!parentV) {return childV}
var ret = {}
extend(ret,parentV)
for(var key$1 in childV) {
var parent = ret[key$1]
var child = child[key$1]
if(parent && !Array.isArray(parent)) {
parent = [parent]
}
ret[key$1] = parent ? parent.concat(child) : Array.isArray(child) ? child : [child]
}
return ret
}
现在对遍历用户传入的配置做一下拆分,那么for-in的代码可以理解为:
for(var key in childV) {
var parent = parentV[key]
var child = childV[key]
if(parent && !Array.isArray(parent)) {
parent = [parent]
}
if(child && !Array.isArray(child)) {
child = [child]
}
ret[key] = parent ? parent.concat(child): child
}
provide
provide 则使用与 data 合并时一样的 mergeDataOrFn函数
, 只要有一侧没有 provide 就不会合并, 原样返回有数据的那一侧.否则返回 mergeDataFn
闭包函数,将在实例化的时候调用
strats.provide = mergeDataOrFn
components、directives、filters
这3个配置项将使用 mergeAssets
合并, 首先将 Vue.options 中对应的属性作为原型创建一个新的对象(之前Vue.extend的分析中了解到初始的Vue.options中是含有默认的 components、directives、filters),然后遍历用户传入的配置,依次添加到这个对象上
var res = Object.create(parent || null)
return extend(red,child)
/************************/
function extend(to,from) {
for(var key in from) {
to[key] = from[key]
}
return to
}
生命周期函数合并
生命周期函数统一使用 mergeHook
合并数据, 处理后的生命周期是一个数组
var LIFECYCLE_HOOLS = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeDestroy',
'destroyed',
'activated',
'deactivated'
]
LIFECYCLE_HOOLS.forEach(function(hook){
strats[hook] = mergeHook
})
/************************/
function mergeHook(parentV,childV) {
var res = childV ? (parentV ? parentV.concat(childV) : Array.isArray(childV) ? childV : [childV]) : [parentV]
return res ? dedupeHooks(res) : res
}
为了简述这个三元表达式,我以 if()
表述帮助分析,大致如下:
- 用户传入了某个生命周期函数并且Vue.options中也有一样的,则把用户传入的添加到数组的末尾.也就是这样形式
created: [fn,fn]
- 用户传入了但是Vue.options上没有,如果不是数组则包装成数组
[ childV ]
,否则原样返回 - 用户没有传入,但Vue.options上有,则把它包装成数组
[ parentV ]
返回
var res = null
if(childV) {
if(parentV) {
res = parentV.concat(childV)
} else {
res = Array.isArray(childV) ? childV : [childV]
}
} else {
res = [parentV]
}
最后再调用去重方法 dedupHooks
, 这部分就不分析了,代码也很简单
function dedupHooks(hooks) {
var res = []
for(var i = 0; i < hooks.length; i++) {
if(res.indexOf(hooks[i] === -1)) {
res.push(hooks[i])
}
}
return res
}
defaultStrat 合并方法
除了 strats 预制的合并方法, 其余的都用 defaultStart 来处理合并, 它内部就是一个三元表达式, 用户有传入配置就使用用户传入的,否则使用Vue.options上的
function defaultStart(parentVal,childVal) {
return childVal === undefind ? parentVal : childVal
}