Vue 源码笔记- mergeOptions 相关

416 阅读3分钟

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 里支持不同写法的 propsinjectdirectives 属性转化成统一的形式
  • 应用 child 里的 mixinextend
  • 将所有的属性的值按照 strats 里对应的合并策略合并成一个值,生成一个新的 options 返回之

所有的 strats 里包含不同属性对应的合并策略,分为以下几类:

  • hooksparentValuechildValue 合并为一个数组中,childValue 中的值顺序靠后
  • assetsfiltercomponentsdirectives):合并之后的值为 { ...Object.create(parentValue), ...childValue }
  • computedpropsmethodsinjects:合并之后的值为{ ...parentValue, ...childValue }
  • watch:合并之后为一个对象,对象的每个属性的合并策略与 hooks 类似,对应的值都是一个数组
  • dataprovides:如果传给 mergeOptions 的第三个参数 vm 不为空,说明是根 vm 的实例化过程,否则,说明是生成组件构造函数的过程。但最终合并之后的值都是一个函数,函数内部先生成 parentValue,然后生成 childValue,然后通过 mergeData 依次将 parentValue 中有但 childValue 中没有的属性依次添加为 childValue 的响应式属性。对于两者都有的,且其值均为对象但并不相等的属性,对两个属性的值再递归调用 mergeData
  • 其它:defaultStrat,以 childValue 优先

好像,除了dataprovides 的合并策略,对于其他的合并策略来将,第三个参数 vm 的作用好像只是为了更详细的报错信息。

全局搜索 mergeOptions( 会发现,它在如下几个地方被调用:

  • 根 vm 的 _init 中生成 vm.$options
  • resolveConstructorOptions 中更新父构造函数的 options
  • Vue.extend 中生成 Sub.options
  • Vue.mixin 中生成 this.options
  • mergeOptions 内部递归处理 mixinsextend 属性时

其中第一个就是生成实例过程中 merge,第二三四都可以算更新或生成构造函数的过程中 merge,第五个则取决于外层调用是属于第一个还是其他三个。