五 Vue 原理解析之 虚拟Dom 到真实Dom的转换过程( 二 )


方法开始生成真实的Dom, VNode 生成真实的Dom 的方式还是分为元素节点和组件两种方式,所以我们使用上一章生成的VNode分别说明 。

五  Vue 原理解析之 虚拟Dom 到真实Dom的转换过程

文章插图
1. 元素节点生成Dom
{// 元素节点VNodetag: 'div',children: [{tag: 'h1',children: [{text: 'title h1'}]}, {tag: 'h2',children: [{text: 'title h2'}]}, {tag: 'h3',children: [{text: 'title h3'}]}]}
大家可以先看下这个流程图有个印象即可,再接下来看具体实现时思路会清晰很多(这里先借用网上的一张图):
开始Dom, 来看下它的定义:
function createElm(vnode, insertedVnodeQueue, parentElm, refElm, nested, ownerArray, index) { ...const children = vnode.children// [VNode, VNode, VNode]const tag = vnode.tag// divif (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {return// 如果是组件结果返回true,不会继续,之后详解createComponent}if(isDef(tag)) {// 元素节点vnode.elm = nodeOps.createElement(tag)// 创建父节点createChildren(vnode, children, insertedVnodeQueue)// 创建子节点insert(parentElm, vnode.elm, refElm)// 插入} else if(isTrue(vnode.isComment)) {// 注释节点vnode.elm = nodeOps.createComment(vnode.text)// 创建注释节点insert(parentElm, vnode.elm, refElm); // 插入到父节点} else {// 文本节点vnode.elm = nodeOps.createTextNode(vnode.text)// 创建文本节点insert(parentElm, vnode.elm, refElm)// 插入到父节点}...}------------------------------------------------------------------nodeOps:export function createElement(tagName) {// 创建节点return document.createElement(tagName)}export function createComment(text) {//创建注释节点return document.createComment(text)}export function createTextNode(text) {// 创建文本节点return document.createTextNode(text)}function insert (parent, elm, ref) {//插入dom操作if (isDef(parent)) {// 有父节点if (isDef(ref)) { // 有参考节点if (ref.parentNode === parent) {// 参考节点的父节点等于传入的父节点nodeOps.insertBefore(parent, elm, ref)// 在父节点内的参考节点之前插入elm}} else {nodeOps.appendChild(parent, elm)//添加elm到parent内}}// 没有父节点什么都不做}这算一个比较重要的方法,因为很多地方会用到 。
依次判断是否是元素节点,注释节点,文本节点,分别创建它们然后插入到父节点里面,这里主要介绍创建元素节点,另外两个并没有复杂的逻辑 。我们接下来看下: 方法定义:
function createChild(vnode, children, insertedVnodeQueue) {if(Array.isArray(children)) {// 是数组for(let i = 0; i < children.length; ++i) {// 遍历vnode每一项createElm(// 递归调用children[i], insertedVnodeQueue, vnode.elm, null, true, // 不是根节点插入children, i)}} else if(isPrimitive(vnode.text)) {//typeof为string/number/symbol/boolean之一nodeOps.appendChild(// 创建并插入到父节点vnode.elm, nodeOps.createTextNode(String(vnode.text)))}}-------------------------------------------------------------------------------nodeOps:export default appendChild(node, child) {// 添加子节点node.appendChild(child)}
开始创建子节点,遍历VNode 的每一项,每一项还是使用之前的方法创建Dom 。如果某一项又是数组,继续调用创建某一项的子节点; 如果某一项不是数组,创建文本节点并将它添加到父节点内 。像这样使用递归的形式将嵌套的VNode全部创建为真实的Dom 。
在看一遍流程图,应该就能减少大家很多疑惑了(这里先借用网上一章图):
简单来说就是由里向外的挨个创建出真实的Dom, 然后插入到它的父节点内,最后将创建好的Dom插入到body内,完成创建的过程,元素节点的创建还是比较简单的,接下来看下组件式怎么创建的 。