mergeOptions 是 Vue 中比较重要的一个函数,它的作用是将 parent options 和 child options 里的各个属性按照某些合并策略生成一个新的 options。这里的 options 就是用于生成 vm 实例的对象。因为 Vue 中创建根 vm 和非根 vm 的方式不同:根 vm 是通过 options 直接生成,而非根 vm 则是先通过 options 生成对应的 constructor,然后再由 constructor 生成 vm。所以 mergeOptions 在实例化过程中和构造函数继承的过程中都涉及,区别是有无传第三个参数 vm 。
function mergeOptions(parent, child, vm) {
if(typof child === 'functions') {
child = child.options
}
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
if(!child._base) {
// 如果存在 extend、mixin 属性,将之合并到 parent 中
if(child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if(child.mixins) {
for(let i = 0, l = child.mixin.length; i < l; ++i) {
parent = mergeOptions(parent, child.mixin[i], vm)
}
}
}
let options = {}
let key
for(key in parent) {
mergeField(key)
}
for(key in child) {
// 同时在 child 和 parent 里的 key 已经在上面得到处理
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
}
mergeOptions 所做的事:
- 将 child 里支持不同写法的 props、inject、directives 属性转化成统一的形式
- 应用 child 里的 mixin 和 extend
- 将所有的属性的值按照
strats里对应的合并策略合并成一个值,生成一个新的 options 返回之
所有的 strats 里包含不同属性对应的合并策略,分为以下几类:
- hooks:parentValue 和 childValue 合并为一个数组中,childValue 中的值顺序靠后
- assets(filter、components、directives):合并之后的值为
{ ...Object.create(parentValue), ...childValue } - computed、props、methods、injects:合并之后的值为
{ ...parentValue, ...childValue } - watch:合并之后为一个对象,对象的每个属性的合并策略与 hooks 类似,对应的值都是一个数组
- data 和 provides:如果传给
mergeOptions的第三个参数 vm 不为空,说明是根 vm 的实例化过程,否则,说明是生成组件构造函数的过程。但最终合并之后的值都是一个函数,函数内部先生成 parentValue,然后生成 childValue,然后通过 mergeData 依次将 parentValue 中有但 childValue 中没有的属性依次添加为 childValue 的响应式属性。对于两者都有的,且其值均为对象但并不相等的属性,对两个属性的值再递归调用 mergeData - 其它:
defaultStrat,以 childValue 优先
好像,除了data 和 provides 的合并策略,对于其他的合并策略来将,第三个参数 vm 的作用好像只是为了更详细的报错信息。
全局搜索 mergeOptions( 会发现,它在如下几个地方被调用:
- 根 vm 的
_init中生成vm.$options时 resolveConstructorOptions中更新父构造函数的options时Vue.extend中生成Sub.options时Vue.mixin中生成this.options时mergeOptions内部递归处理 mixins 和 extend 属性时
其中第一个就是生成实例过程中 merge,第二三四都可以算更新或生成构造函数的过程中 merge,第五个则取决于外层调用是属于第一个还是其他三个。