# options的合并策略
在Vue初始化之前,内部默认已经给Vue.options添加了一些全局的配置选项,包括全局的components、directives、filters,以及一个_base属性(指向Vue构造函数)
在组件初始化时,会先调用组件初始化方法_init (opens new window),在其内部,会对自身的options以及父类的options进行合并。
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// merge options
if (options && options._isComponent) { // 子组件options合并, 子组件初始化
initInternalComponent(vm, options)
} else {vm.$options = mergeOptions(
// // resolveConstructorOptions:获取父类构造函数的options选项(Vue.options)
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
选项合并,内部调用的就是mergeOptions (opens new window)方法。
options的合并主要包括:包括data、components、hook等,由于每种合并的方式不同,因此在Vue中,采用了策略模式,针对不同的key,采用不同的合并方式。
// 1 data选项的合并
strats.data = function() {}
// 2 生命周期hook的合并
const LIFECYCLE_HOOKS = [
'beforeCreate', 'created', 'beforeMount', 'mounted',
'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed'
];
function mregeHook () {};
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
});
// 3、组件components、指令directives、过滤器filters的合并
function mergeAssets () {}
const ASSET_TYPES = ['component', 'directive', 'filter'];
ASSET_TYPES.forEach(function (type) {
strats[type + 's'] = mergeAssets
});
// 4、watch选项的合并
strats.watch = function (){}
// 5、props、methods、computed、injecte的合并
strats.props =
strats.methods =
strats.inject =
strats.computed = function () {}
// 6 provide合并
strats.provide = mergeDataOrFn
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 生命周期hook的合并
对于Vue生命周期的合并,为了我们更好的理解其过程,我们对mergeOptions函数,做了一些简化,只抽取其中关于lifeCycle的部分,其他部分可以看源码 (opens new window)。
- 声明一个策略对象
strats,根据不同的key,定义不同的函数strats[key] = fn。这样就可以针对不同的key,采用不同的合并策略。如果没有,则采用默认的合并策略defaultStrat。
const LIFECYCLE_HOOKS = [
'beforeCreate', 'created', 'beforeMount', 'mounted',
'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed'
];
// 默认合并策略, childVal有,则采用childVal,否则采用parentVal
const defaultStrat = function (parentVal: any, childVal: any): any {
return childVal === undefined ? parentVal : childVal
}
const strats = {};
// 生命周期的合并
function mergeHook(parentVal, childValue) {
if(childValue){
if(parentVal){
return parentVal.concat(childValue);
}else{
return [childValue];
}
}else{
return parentVal;
}
}
// 自定义生命周期合并策略
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})
function mergeOptions(parent, child) {
// 遍历父options
const options = {};
for (let key in parent) {
mergeField(key);
}
// 处理子options有,而父options没有的
for (let key in child) {
if (!parent.hasOwnProperty(key)) {
mergeField(key);
}
}
function mergeField(key) {
// 根据key 不同的策略来进行合并 ,当前只考虑生命周期
if (strats[key]) {
options[key] = strats[key](parent[key], child[key]);
} else {
// 默认合并
options[key] = defaultStrat(parent[key], child[key]);
}
}
// 将最终的合并结果options返回
return options;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 生命周期的执行
在组件初始化阶段,调用mergeOptions,将vm的options与其构造函数的options合并,并赋值给vm.$options,源码地址 (opens new window),在执行对应的操作后,即可调用对应的hooks钩子。
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
if (options && options._isComponent) { // 子组件options合并, 子组件初始化
initInternalComponent(vm, options)
} else {vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
if (vm.$options.el) {
// 调用moutComponent方法,挂载组件,调用callHook(vm, 'beforeMount')
// 渲染完成后,调用callHook(vm, 'mounted'),
vm.$mount(vm.$options.el);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
合并完成后,vm实例的生命周期钩子函数(options上定义的),就都挂载到了vm.$options对象上了。当我们需要执行钩子的时候,调用callhook (opens new window)方法即可。
function callhook(vm ,hook) {
const handlers = vm.$options[hook]; // vm.$options.created = [ fn1,fn1,... ]
if(handlers){
for(let i = 0; i<handlers.length;i++){
// 更改生命周期中的this
handlers[i].call(vm);
}
}
}
2
3
4
5
6
7
8
9
# components、directives、filters的合并
components的合并主要包括2部分:
- 通过原型链的方式,链接childVal与parentVal,即
const res = Object.create(parentVal || null)
vue自定义组件渲染时,首先是在自身选项vm.options.components上查找对应的component,如果找不到,则去父类options.components上查找。因此,通过原型链的方式,可以链接全局组件与局部组件。
- 循将childVal,将对应的key、value赋值给res,并返回。
function mergeAssets (
parentVal: ?Object,
childVal: ?Object,
vm?: Component,
key: string
): Object {
// 基于原型链,连接子类options中的components与父类options,
// 例如组件components选项:child.options.components.__proto___ == parent.options.components,
// 这样如果父子类有同名的components,则依照就近原则,先查找子类选项的components,找不到,则取父类options.components上去找
const res = Object.create(parentVal || null)
if (childVal) {
process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm)
return extend(res, childVal)
} else {
return res
}
}
// 3、组件components、指令directives、过滤器filters的合并策略
const ASSET_TYPES = ['component', 'directive', 'filter'];
ASSET_TYPES.forEach(function (type) {
strats[type + 's'] = mergeAssets
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在组件初始化阶段,调用mergeOptions,将vm的options与其构造函数的options合并,并赋值给vm.$options,源码地址 (opens new window),在执行对应的操作后,即可调用对应的hooks钩子。
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// merge options 选项合并
if (options && options._isComponent) {
// 子组件options合并及初始化
// initInternalComponent(vm, options)
} else {
// resolveConstructorOptions:获取父类构造函数的options选项
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
// 执行钩子函数
callHook(vm, 'beforeCreate');
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 合并完成后,vm实例的生命周期钩子函数,就都挂载到了
vm.$options对象上了。当我们需要执行钩子的时候,调用callhook (opens new window)方法即可。
function callhook(vm ,hook) {
const handlers = vm.$options[hook]; // vm.$options.created = [ fn1,fn1,... ]
if(handlers){
for(let i = 0; i<handlers.length;i++){
handlers[i].call(vm); // 更改生命周期中的this
}
}
}
2
3
4
5
6
7
8
# Vue.mixin原理
Vue.mixin方法的主要作用就是:将用户传入的options,与全局的Vue.options合并,源码参考 (opens new window)如下:
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
2
3
4
内部主要调用的Vue的核心一个核心API:mergeOptions (opens new window)