在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)
}
}
patch方法
在_update中,既有实现更新Dom的操作,也有初始化Dom的操作。
patch方法,就是对Dom进行操作的核心方法。
在vdom文件夹下创建一个patch.js,用来处理相关逻辑。
在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' } }
}
});
更新前页面
更新后页面
更新后,能看到{{arr}}
已经被替换成data
中的数据了。
当然,这里的样式都丢失了,将会在后面的学习中,一一完善。