Skip to content
On this page

合并配置

在 Vue 执行过程中有两处时机进行 配置合并

  • new Vue() 初始化实例过程
  • 初始化子组件的过程

首先分析 Vue.mixin 方法

js
// global-api/mixin.js
function initMixin (Vue) {
  // 注意这里调用的Vue的静态方法,其中this类似Vue.extend方法中的this
  Vue.mixin = function (mixin) {
    this.options = mergeOptions(this.options, mixin)
    return this
  }
}

这里的initMixin 是传入mixin选项,赋值到Vue上

js
// instance/init.js
function initMixin (Vue) {
  Vue.prototype._init = function (options) {
    ...
    if (options && options._isComponent) {
      initInternalComponent(vm, options) // 组件的配置合并
    } else {
      vm.$options = mergeOptions(
      	resolveConstructorOptions(vm.constructor), // 传入组件实例构造器函数(Vue)
        options || {},
        vm
      )
      // 这里是外部调用初始化实例的配置合并
      // new Vue(options)
    }
    ...
  }
}

在初始化 Vue 实例过程中

resolveConstructorOptions 分析

js
function resolveConstructorOptions (Ctor) {
  // Ctor 构造器函数
  let options = Ctor.options
  if (Ctor.super) {
    // 这里是通过Vue.extend方法返回的构造器函数,进行实例化时执行的
  }
  return options // 返回构造器的options (Vue.options)
}

mergeOptions 的实现

js
// util/options.js
function mergeOptions (parent, child, vm) {
  // 一些选项的格式化
  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)
  
  if (!child._base) {  
    // 这里用_base保证传入的options不是另一个 mergeOptions 过的结果
    // 因为 Vue.options._base = Vue
    
    // 针对传入的options中,如果包含了mixins选项和extends选项
    // 先递归合并到parent选项中
    if (child.extends) {
      parent = mergeOptions(parent, child.extend, 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
    // 分别遍历 parent 和child 的 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
  }
}

在组件实例初始化过程中

initInternalComponent 分析

js
// instance/init.js
function initInternalComponent (vm, options) {
  // 以构造器的options 为原型构造
  // options 是在初始化组件vm实例时,调用组件的init hook传入的
  /**      
  	options = {
      _isComponent: true,
      _parentVnode: vnode, 占位vnode
      parent: parent, 父 vm 实例
  	}
  **/
  // 通过Vue.extend返回的合并后的构造器的options
  const opts = vm.$options = Object.create(vm.constructor.options)
  const parentVnode = options._parentVnode
  opts.parent = options.parent
  opts._parentVnode = parentVnode

  // componentOptions 在创建占位vnode 传入
  // { Ctor, propsData, listeners, tag, children }
  const vnodeComponentOptions = parentVnode.componentOptions
  opts.propsData = vnodeComponentOptions.propsData
  opts._parentListeners = vnodeComponentOptions.listeners
  opts._renderChildren = vnodeComponentOptions.children
  opts._componentTag = vnodeComponentOptions.tag

  if (options.render) {
    opts.render = options.render
    opts.staticRenderFns = options.staticRenderFns
  }
}