菜鸟初探Vue源码(五)-- 合并配置

218 阅读1分钟

Vue.js 在初始化阶段,会执行一些配置的合并。将所有配置合并到$options上。主要分为外部调用场景下的配置合并(也就是执行new Vue()时)和组件场景的配置合并(也就是子组件初始化时)。

// merge options
if (options && options._isComponent) {
    initInternalComponent(vm, options)
} else {
    vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor), // Vue.options
        options || {}, // vm.options
        vm
    )
}

在外部调用场景中(new Vue()时),由于options._isComponent不存在,因此执行 else 的逻辑。非常明确,使用mergeOptions函数将配置合并到$options上。接下来看一下是如何合并的。

export function resolveConstructorOptions (Ctor: Class<Component>) {
    let options = Ctor.options
    if (Ctor.super) {
        // 此时Vue.super为undefined,所以不进入if判断
    }
    // 该函数返回Vue.options
    return options
}

第一个参数resolveConstructorOptions(vm.constructor)Vue.super不存在,因此直接返回Vue.options作为第一个参数。

export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
    // Apply extends and mixins on the child options,
    // but only if it is a raw options object that isn't
    // the result of another mergeOptions call.
    // Only merged options has the _base property.
    if (!child._base) {
        if (child.extends) {
            parent = mergeOptions(parent, child.extends, vm)
        }
        if (child.mixins) {
            for (let i = 0, l = child.mixins.length; i < l; i++) {
                parent = mergeOptions(parent, child.mixins[i], vm)
            }
        }
    }
    const options = {}
    let key
    for (key in parent) {
        mergeField(key)
    }
    for (key in child) {
        if (!hasOwn(parent, key)) {
            mergeField(key)
        }
    }
    function mergeField (key) {
        // strats是config的属性(是一个对象),对象中定义了不同的方法。对于不同的key值(比如data、watch等等)执行不同的合并策略,此处是获取相应的方法
        const strat = strats[key] || defaultStrat
        options[key] = strat(parent[key], child[key], vm, key)
    }
    return options
}

mergeOptions中,首先对用户传递的配置对象的 mixinsextends 递归调用mergeOptions。 紧接着对 Vue.optionsoptions 执行mergeField,在mergeField中针对不同的属性执行不同的合并策略。最终将合并好的配置返回赋值给$options。接下来看组件中的配置合并。

export function initInternalComponent (vm: Component, options: InternalComponentOptions) {
  const opts = vm.$options = Object.create(vm.constructor.options)
  opts.parent = options.parent
  opts._parentVnode = options._parentVnode
  // ...
  if (options.render) {
    opts.render = options.render
    opts.staticRenderFns = options.staticRenderFns
  }
}

组件的配置执行了initInternalComponent(vm, options)函数。以Vue.options为原型( Vue 为子组件的构造器)创建对象,赋值给$options。再扩展一些其他的属性。以下Vue.extend是对子组件构造器处理时的mergeOptions

Vue.extend = function (extendOptions: Object): Function {
    extendOptions = extendOptions || {}
    const Super = this
    const Sub = function VueComponent (options) {
      this._init(options)
    }
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    // ...
    return Sub
}