vue源码学习10: 将虚拟dom创建成真实dom

2,012 阅读2分钟

vue源码学习9: 虚拟Dom实现原理中,通过vm._render生成了vnode。

今天的学习内容,就是如何把vnode转换成真实Dom插入页面中。

_update中的patch方法

在lifecycleMixin中,Vue的原型上面挂载了一个_update方法,这个方法接受一个参数,就是已经生成的vnode

export function lifecycleMixin(Vue) {
    Vue.prototype._update = function (vnode) {
        console.log('vnode', vnode)
        // 即有初始化,又有更新
        // 比较前后的虚拟节点的差异
        const vm = this
        patch(vm.$el, vnode)
    }
}

image.png

patch方法

在_update中,既有实现更新Dom的操作,也有初始化Dom的操作。

patch方法,就是对Dom进行操作的核心方法。

在vdom文件夹下创建一个patch.js,用来处理相关逻辑。

image.png

在patch中,接受两个参数,oldVnode和vnode,oldVnode代表旧的Dom,vnode代表新的Dom。

如果oldVnode的nodeType等于1,则说明是真实元素,就需要做如下操作:

  • 根据当前的vnode查找它的父节点
  • 在这个节点后面,插入转换后的Dom
  • 移除原来的vnode
export function patch(oldVnode, vnode) {
    if (oldVnode.nodeType == 1) {
        console.log('真实元素')
        // 用Vnode 替换真实 Dom
        const parentEl = oldVnode.parentNode;
        let elm = createElm(vnode)
        parentEl.insertBefore(elm, oldVnode.nextSibling)
        parentEl.removeChild(oldVnode)
    }
}

createElm方法

如果传入的vnode的tag是string,则说明是一个element,否则就是一个text

根据这两种情况分别进行创建新的Dom

  • 如果tag是string,则创建element,且循环其children
  • 如果tag是undefined,则只需要创建text节点

最后,返回enode.el,此时页面将会被渲染。

function createElm(vnode) {
    let { tag, data, children, text, vm } = vnode
    if (typeof tag === 'string') {
        // 虚拟节点会有一个el属性,对应真实节点
        vnode.el = document.createElement(tag)
        children.forEach(child => {
            vnode.el.appendChild(createElm(child))
        })
    } else {
        vnode.el = document.createTextNode(text)
    }
    return vnode.el

}

结果图片

Vue代码片段

<div id="app" a="111" style="color: red;background: green;">
    hello{{arr}}word
</div>

let vm = new Vue({
    el: '#app',
    data() {
        return { arr: { name: 'qian' } }
    }
});

更新前页面

image.png

更新后页面

image.png

更新后,能看到{{arr}}已经被替换成data中的数据了。

当然,这里的样式都丢失了,将会在后面的学习中,一一完善。

历史文章