5.3 组件Vnode渲染真实DOM
有了完整的Vnode tree
,接下来是根据Vnode tree
渲染真实的DOM
5.3.1 真实节点渲染流程图
5.3.2 具体流程分析
- 经过
vm.render()
生成完整的Virtual Dom
树后,紧接着执行Vnode渲染真实DOM的过程,即vm.update()
,而update
的核心方法是vm._patch
- 经过
vm.patch
内部会通过createElm
去创建真实的DOM
元素,期间遇到子Vnode
会递归调用createElm
方法。
- 递归调用过程中,判断该节点类型为组件类型是通过
createComponent
方法判断的,该方法和渲染Vnode
阶段的方法createComponent
不同,他会调用子组件的init
初始化钩子函数,并完成组件的DOM
插入。
- 递归调用过程中,判断该节点类型为组件类型是通过
- 而
init
初始化钩子函数的核心是new
实例化这个子组件,实例化子组件的过程又回到合并配置,初始化生命周期,初始化事件中心,初始化渲染的过程
- 而
- 完成所有子组件的实例化和节点挂载后,最后才回到根节点的挂载。
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
方法中,createComponent
的核心是会判断这个Vnode
是否为子组件,如果条件满足,则执行组件注册时安装的init
方法(由于在组件注册过程中会安装一系列的钩子函数,所以是否有钩子函数可以作为判断组件的唯一条件)。
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
var i = vnode.data;
// 是否有钩子函数可以作为判断是否为组件的唯一条件
if (isDef(i = i.hook) && isDef(i = i.init)) {
// 执行init钩子函数
i(vnode, false /* hydrating */);
}
···
}
由于前面在介绍组件内部钩子函数时跳过了每个钩子内部实现功能的介绍,所以我们需要回头分析 init
钩子函数的执行逻辑(其中忽略keeplive
分支逻辑)。
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)
}
init
执行过程中会调用createComponentInstanceForVnode
对子组件进行实例化。调用createComponent
是一个递归调用实例化所有子组件的过程,只有会将所有的子组件实例化,并挂载到对应父节点上后,才最后进行根节点的挂载。