Vue源码解析: 丰富的选项合并策略

  • Post author:
  • Post category:vue




简介



Vue

的初始化过程中,最开始的阶段就是选项合并阶段。它通过调用

mergeOptions

函数将两个选项配置合并成一个选项配置。这里的选项

options

的形式实际上就是我们平时开发时在

Vue

中写的对象配置,形式如下:

{ 
  components: {}, 
  filters: {},
  data() { return {} }, 
  computed: {}, 
  created: {}, 
  methods: {},
  ... 
}

因此,选项合并实际可以简单的看作是两个上面的对象合并成一个对象。

由于

mergeOptions

是实现

实例化

(

new Vue(options)

)、

继承

(

Vue.extend

)和

混入

(

Vue.mixin

)三大功能的核心函数,所以分析它的实现是理解

Vue

实例化过程和继承的必经之路。 下面我们将从以下几个方面来全面了解

Vue

中的选项合并:

  1. 实例化过程中的选项,了解我们需要合并的选项是怎样的。


  2. mergeOptions

    的实现,了解各个合并策略。

  3. 继承(

    Vue.extend



    extends:{}

    )的选项合并。

  4. 混入(

    Vue.mixin



    mixins:[]

    )的选项合并。

  5. 为什么实例化过程中有时用

    initInternalComponent

    而不是

    mergeOptions



实例化过程中的选项

从上一节的学习我们知道,

Vue

的实例化过程调用的是

core/instance/init.js

文件中的

_init

方法。

Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
  
  	...
  
    vm.$options = mergeOptions(
      resolveConstructorOptions(vm.constructor),
      options || {},
      vm
    )
  
  	...
  }

实例化的过程中第一个重要的处理就是选项的合并,这里第二个参数比较容易理解,就是我们平时写的

Vue

的配置项。第一个参数则是通过

resolveConstructorOptions(vm.constructor)

生成,找到

resolveConstructorOptions

方法,代码如下

// * 返回构造函数的 options
export function resolveConstructorOptions (Ctor: Class<Component>) {
  // * 如果不是继承,options 就是原构造函数的 options
  // * 如果是继承时,options 为合并 superOptions 和 extendOptions 的 options
  // * 此外,这里的 options 还包含了全局注册的 组件/指令/过滤器
  let options = Ctor.options
  // * Ctor.super 存在说明是调用了 extend 方法进行继承生成的构造函数
  // * 详见 /src/core/global-api/extend.js 文件
  // * - superOptions 是父类 options
  // * - extendOptions 是当前类传入的 options (如果与 sealedOptions不同,需要合并)
  // * - options = mergeOptions(superOptions, extendOptions)
  // * - sealedOptions 保存的是当前类继承时 合并后的 options(是extend的时候赋值的)
  if (Ctor.super) {
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions
    // * 如果 superOptions 变动了,需要处理新的 options
    if (superOptions !== cachedSuperOptions) {
      Ctor.superOptions = superOptions
      // * sealedOptions 是 seal 的时候赋值的,
      // * 这里的变动可能是 options 在 extend 后继续被赋值
      // * 复现:https://jsfiddle.net/vvxLyLvq/2/
      // * 所以需要找出变动了的属性,然后更新到 extendOptions 上
      // * 这里的 extend 只是对象的合并
      const modifiedOptions = resolveModifiedOptions(Ctor)
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }
      // * 由于 options 变化了,重新合并一次
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        // * 将自身的构造函数也存到了 components 对象中
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}

这里传入的是当前构造函数,那么

Ctor.options

指的是

Vue

构造函数的

options

,还记得上一节中提到的

Vue.options

吗?这里将

options

的内容从上一节中复制过来,代码如下:

// Vue.options 内容
{
	components: {
  	KeepAlive,
    // 新增 platformComponents
    Transition,
    // 新增 platformComponents
    TransitionGroup
  },
  filters: {},
  directives: {
  	// 新增 platformDirectives
    model,
    // 新增 platformDirectives
    show
  },
  _base: Vue
}

再看下一句

Ctor.super

的判断,

super

这个字段是在

core/global-api/extend.js

文件中的

extend

方法调用时添加的。如果

Ctor.super

存在,说明

Ctor

是通过继承而来的子构造函数。**但是,**

如果在


**extend**


后,我们又在父构造函数的


**options**


上添加新的属性,这个时候子构造函数是无法继承新的属性的。因此,这里需要通过


**Ctor.super**


向上寻找,找出所有父构造函数更新的


**options**


属性,并更新到子构造函数上,这样就能解决


**Vue.options**

**被更改的问题了。**有兴趣的话,可以看一下

Vue



issues#4976

最后,经过

resolveConstructorOptions

处理后,最终得到的同样是一个

Vue

的配置选项,下一步则是需要将这两个配置选项进行合并了。




mergeOptions



版权声明:本文为qq_42108498原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。