简介
在
Vue
的初始化过程中,最开始的阶段就是选项合并阶段。它通过调用
mergeOptions
函数将两个选项配置合并成一个选项配置。这里的选项
options
的形式实际上就是我们平时开发时在
Vue
中写的对象配置,形式如下:
{
components: {},
filters: {},
data() { return {} },
computed: {},
created: {},
methods: {},
...
}
因此,选项合并实际可以简单的看作是两个上面的对象合并成一个对象。
由于
mergeOptions
是实现
实例化
(
new Vue(options)
)、
继承
(
Vue.extend
)和
混入
(
Vue.mixin
)三大功能的核心函数,所以分析它的实现是理解
Vue
实例化过程和继承的必经之路。 下面我们将从以下几个方面来全面了解
Vue
中的选项合并:
-
实例化过程中的选项,了解我们需要合并的选项是怎样的。
-
mergeOptions
的实现,了解各个合并策略。 -
继承(
Vue.extend
和
extends:{}
)的选项合并。 -
混入(
Vue.mixin
和
mixins:[]
)的选项合并。 -
为什么实例化过程中有时用
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
mergeOptions