打个小广告哈,最近做了个小程序,来测测你的工作性价比吧~(微信扫码打开小程序即可)
上一节可以看成是merge数据前期准备,下面就介绍到了我们的mergeOption
的重头戏啦——merge。先看代码(仍然是mergeOptions里面的代码):
const options = {
}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
可以看到,首先定义了options,再给这个options赋值,最后返回options。这就是mergeOptions方法主要干的事。那么,我们知道,Vue提供的属性很多,比如:el, data, props, filters … 这些属性是怎么来merge的呢?是不是有什么合并的规则,每个属性有自己的对应合并规则呢?
是的。
这段代码有两个for循环,for循环内部都使用了mergeField函数。我们先来看看这个函数:
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
该函数接受了一个key作为参数,这个key就是属性名称。然后定义了一个strat
变量,它的值是strats[key] || defaultStrat
,也就是说,会在starts
这个对象中找有没有这个key对应的值,有就用这个值,没有的话就用defaultStrat
作为默认值。所以,这个strats
可以看成我们上面说的合并规则集。它的定义就在当前文件中,我们一点一点来看。
合并策略
/**
* Option overwriting strategies are functions that handle
* how to merge a parent option value and a child option
* value into the final value.
*
* 选项重写策略是一堆函数, 它们负责把一个父选项和一个子选项合并成一个最终的值
*/
const strats = config.optionMergeStrategies
首先在上面定义了这个对象。它的值是config.optionMergeStrategies
,顺着依赖找到这个定义可以发现他就是一个空对象: Object.create(null)
。所以此时: strats = {}
el和propsData
然后开始给它添加规则了,先是这两个:
/**
* Options with restrictions
*
* el 和 propsData 的合并策略一样
* 注意: 它们只能给使用 new 操作符 创建的 Vue 实例使用
*/
if (process.env.NODE_ENV !== 'production') {
strats.el = strats.propsData = function (parent, child, vm, key) {
if (!vm) {
warn(
`option "${
key}" can only be used during instance ` +
'creation with the `new` keyword.'
)
}
return defaultStrat(parent, child)
}
}
很简单,就是给 el
和 propsData
设置合并规则——默认的规则。慢着,我们先看一下这个默认合并策略是什么吧,毕竟出现两次了,这也是在当前文件中定义的:
/**
* Default strategy.
*/
const defaultStrat = function (parentVal: any, childVal: any): any {
return childVal === undefined
? parentVal
: childVal
}
默认的策略很简单,就是有孩子用孩子,没孩子用爸爸
。也就是无第二个参数时就使用第一个参数的值。
Ok,继续看
data
接下来是data的合并规则:
strats.data = function (
parentVal: any,
childVal: any,
vm?: Component
): ?Function {
if (!vm) {
if (childVal && typeof childVal !== 'function') {
process.env.NODE_ENV !== 'production' && warn(
'The "data" option should be a function ' +
'that returns a per-instance value in component ' +
'definitions.',
vm
)
return parentVal
}
return mergeDataOrFn(parentVal, childVal)
}
return mergeDataOrFn(parentVal, childVal, vm)
}
首先的判断的 if(!vm)
, 还有内部的两个return,参数列表只少了个vm
,那么有vm和没有vm有什么不一样吗?
首先,不一样是肯定的,现在你可以简单的理解为,没有vm的操作的是子组件。
继续,如果没有提供vm
的话,进入if分支,接着又是一个if:childVal && typeof childVal !== 'function'
,你可能不太了解这个判断,但是你肯定认识这段警告的内容:data选项要是一个函数。是不是很熟悉?
如果满足data是函数的话,则会执行:return mergeDataOrFn(parentVal, childVal)
。当然,如果提供了vm的话,也会执行: mergeDataOrFn(parentVal, childVal, vm)
。那么就来看看这个mergeDataOrFn
的真面目吧。
/**
* Data
*/
export function mergeDataOrFn (
parentVal: any,
childVal: any,
vm?: Component
): ?Function {
if (!vm) {
// in a Vue.extend merge, both should be functions
// 在 Vue.extend 的合并中,两个都应该是函数
if (!childVal) {
return parentVal
}
if (!parentVal) {
return childVal
}
// when parentVal & childVal are both present,
// we need to return a function that returns the
// merged result of both functions... no need to
// check if parentVal is a function here because
// it has to be a function to pass previous merges.
// 当 parentVal 和 childVal 都存在的时候,我们需要返回一个函数,这个函数返回这两个对象
// 合并的结果。这里不需要检查parentVal是不是函数,因为它只有是函数才可以通过之前的合并。
return function mergedDataFn () {
return mergeData(
typeof childVal === 'function' ? childVal.call(this, this) : childVal,
typeof