5.3 组件Vnode渲染真实DOM
根据前面的分析,不管是全局注册的组件还是局部注册的组件,组件并没有进行实例化,那么组件实例化的过程发生在哪个阶段呢?我们接着看Vnode tree
渲染真实DOM
的过程。
5.3.1 真实节点渲染流程图
5.3.2 具体流程分析
- 经过
vm._render()
生成完整的Virtual Dom
树后,紧接着执行Vnode
渲染真实DOM
的过程,这个过程是vm.update()
方法的执行,而其核心是vm.__patch__
。 vm.__patch__
内部会通过createElm
去创建真实的DOM
元素,期间遇到子Vnode
会递归调用createElm
方法。- 递归调用过程中,判断该节点类型是否为组件类型是通过
createComponent
方法判断的,该方法和渲染Vnode
阶段的方法createComponent
不同,他会调用子组件的init
初始化钩子函数,并完成组件的DOM
插入。 init
初始化钩子函数的核心是new
实例化这个子组件并将子组件进行挂载,实例化子组件的过程又回到合并配置,初始化生命周期,初始化事件中心,初始化渲染的过程。实例挂载又会执行$mount
过程。- 完成所有子组件的实例化和节点挂载后,最后才回到根节点的挂载。
__patch__
核心代码是通过createElm
创建真实节点,当创建过程中遇到子vnode
时,会调用createChildren
,createChildren
的目的是对子vnode
递归调用createElm
创建子组件节点。
// 创建真实dom
function createElm (vnode,insertedVnodeQueue,parentElm,refElm,nested,ownerArray,index) {
···
// 递归创建子组件真实节点,直到完成所有子组件的渲染才进行根节点的真实节点插入
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
return
}
···
var children = vnode.children;
//
createChildren(vnode, children, insertedVnodeQueue);
···
insert(parentElm, vnode.elm, refElm);
}
function createChildren(vnode, children, insertedVnodeQueue) {
for (var i = 0; i < children.length; ++i) {
// 遍历子节点,递归调用创建真实dom节点的方法 - createElm
createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i);
}
}
createComponent
方法会对子组件Vnode
进行处理中,还记得在Vnode
生成阶段为子Vnode
安装了一系列的钩子函数吗,在这个步骤我们可以通过是否拥有这些定义好的钩子来判断是否是已经注册过的子组件,如果条件满足,则执行组件的init
钩子。
init
钩子做的事情只有两个,实例化组件构造器,执行子组件的挂载流程。(keep-alive
分支看具体的文章分析)
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
var i = vnode.data;
// 是否有钩子函数可以作为判断是否为组件的唯一条件
if (isDef(i = i.hook) && isDef(i = i.init)) {
// 执行init钩子函数
i(vnode, false /* hydrating */);
}
···
}
var componentVNodeHooks = {
// 忽略keepAlive过程
// 实例化
var child = vnode.componentInstance = createComponentInstanceForVnode(vnode,activeInstance);
// 挂载
child.$mount(hydrating ? vnode.elm : undefined, hydrating);
}
function createComponentInstanceForVnode(vnode, parent) {
···
// 实例化Vue子组件实例
return new vnode.componentOptions.Ctor(options)
}
显然Vnode
生成真实DOM
的过程也是一个不断递归创建子节点的过程,patch
过程如果遇到子Vnode
,会优先实例化子组件,并且执行子组件的挂载流程,而挂载流程又会回到_render,_update
的过程。在所有的子Vnode
递归挂载后,最终才会真正挂载根节点。