vue虚拟DOM


模板转为视图过程

Vue.js 2.0开始引入Virtual DOM,Vue.js通过编译将template 模板转换成渲染函数(render ) ,执行渲染函数就可以得到一个虚拟节点树。在对 Model 进行操作的时候,会触发对应Watcher对象。Watcher对象会调用对应的update来修改视图。这个过程主要是将新旧虚拟节点进行差异对比,然后根据对比结果进行DOM操作来更新视图。
简单点讲,在Vue的底层实现上,Vue将模板编译成虚拟DOM渲染函数。在状态改变时,Vue计算出重新渲染组件的最小代价并应到DOM操作上。

1
2
3
4
5
1、渲染函数:渲染函数是用来将template模板生成Virtual DOM的。
2、VNode 虚拟节点: 通过 createElement 方法能将 VNode 渲染成 dom 节点。
3、patch(也叫做patching算法):虚拟DOM最核心,可以将vnode渲染成真实的DOM,这个过程是对比新旧虚拟节点,然后根据对比结果找出需要更新的的节点进行更新。
4、patch 的核心----diff 算法,找出本次DOM需要更新的节点来更新,其他的不更新。
5、虚拟DOM的最终目标是将虚拟节点渲染到视图上。
为什么需要虚拟DOM
1
2
3
4
1、操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。
DOM操作的执行速度远不如JS的运算速度快,因此,把DOM操作搬运到JS中,运用patching算法来计算出真正需要更新的节点,减少DOM操作,提高性能。
2、提升渲染性能
在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。
diff算法

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把所记录的差异应用到所构建的真正的DOM树上,视图就更新了

diff算法实现过程

1、让vNode渲染成真正的DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function createElement(vnode) {    
var tag = vnode.tag
var attrs = vnode.attrs || {}
var children = vnode.children || []
if (!tag) {
return null
}
// 创建真实的 DOM 元素
var elem = document.createElement(tag)
// 属性
var attrName
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
// 给 elem 添加属性
elem.setAttribute(attrName, attrs[attrName])
}
}
// 子元素
children.forEach(function (childVnode) {
// 给 elem 添加子元素,如果还有子节点,则递归的生成子节点。
elem.appendChild(createElement(childVnode)) // 递归
}) // 返回真实的 DOM 元素
return elem
}

2、考虑vnode与newVnode如何对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function updateChildren(vnode, newVnode) {
var children = vnode.children || []
var newChildren = newVnode.children || []
// 遍历现有的children
children.forEach(function (childVnode, index) {
var newChildVnode = newChildren[index]
// 两者tag一样
if (childVnode.tag === newChildVnode.tag) {
// 深层次对比,递归
updateChildren(childVnode, newChildVnode)
} else {
// 两者tag不一样
replaceNode(childVnode, newChildVnode)
}
}
)}