在Vue的_init
方法中,合并选项是通过下面的代码实现的
// merge options
// 选项合并
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
从上面if else
能看出来,合并策略有两个,当选项存在并且_isComponent
为true时,执行的是initInternalComponent
,否则执行mergeOptions
。那么什么情况下_isComponent
会为true呢?
两种策略
我们用一个简单的例子来分析一下这两个策略执行的时机。
<div id="demo">
<comp :msg="msg" @log-msg="logMsg"></comp>
</div>
<script>
Vue.component('comp', {
props: ['msg'],
template: `
<div class="blog-post">
<h3>{{ msg }}</h3>
<button @click="$emit('log-msg', msg)">
Console Message
</button>
`
});
// 创建实例
const app = new Vue({
el: '#demo',
data: {
msg: 'props-message'
},
methods: {
logMsg(data) {
console.log(data)
}
}
});
</script>
首先,我们分别在选项合并的if和else里分别打个断点,看看他执行的逻辑。
当我们重新刷新页面,发现首先进入的逻辑是else里的mergeOptions:
而从它的调用栈call Stack
中可以回溯到真正来源是一个匿名函数,而这个匿名函数其实就是new Vue:
因此,mergeOption其实是在根实例初始化时执行的策略。
当断点进入initInternalComponent
中时,它的调用栈call Stack
就比较复杂,我们来一步步拆解一下:
-
new Vue
时,根实在this._init
函数中执行例执行$mount方法 - $mount获取到temple模板后,通过
compileToFunctions
函数获取render函数,处理完后执行真正的mount挂载函数。 - mount的本质其实是执行了mountComponet方法
- mountComponet中主要做了两件事,定义了updateComponent方法,创建组件对应的watcher。而updateComponent作为参数传给Watch,Watcher的构造函数中,在执行get方法时会调用他。
- updateComponent函数本质是执行vm实例上的_update方法,他是通过一个patch函数,来计算出新旧vnode节点的差异,并执行对应的更新操作
- 因为初始化时,没有旧的节点,因此直接根据新的vnode,直接执行创建dom元素的createElm函数,如果发现这个vnode是个自定义组件,则执行createComponent函数
- 因为自定义组件不是<div>标签,能够直接生成dom树,他需要创建一个组件实例,像根实例一样,在这个组件实例的基础上执行mount方法,向下递归直到该组件中所有vonde都能直接转为dom,这样的结构其实就是Vue教程中讲的组件的组织
- 在创建组件实例的
createComponentInstanceForVnode
方法中,我们最终找到了这个_isComponent
属性,当他去创建组件实例(VueComponent)的时候,就会又执行我们熟悉的_init
方法,这时候我们的option里就有_isComponent
属性了。因此,initInternalComponent是创建组件实例时执行的策略
从上面的分析可知,这两种合并策略一种是为Component
组件的情况下,在从vnode创建组件实例是,会执行initInternalComponent
进行内部组件配置合并;一种是非组件的情况,即根实例创建时,直接通过mergeOptions
做配置合并。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。