虚拟DOM

虚拟DOM是什么?

虚拟DOM其实就是在原有DOM的基础中,在js中再做一层DOM的抽象。

虚拟DOM有什么用?

在一些需要大量更改DOM的情况下,比如更新表格内的内容,重新排序等,会引起重绘、重排、引起大量的性能消耗,虚拟DOM就是针对这个问题的一个解决方案,在更改前都是针对虚拟DOM进行操作,不对真实DOM进行更改,更改完成后使用diff算法对比DOM树,只操作需要更改的DOM节点,减小 性能开销。

虚拟DOM怎么用?

首先需要创建一个类似的虚拟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
25
26
class VNode {
constructor(tag, children, text) {
this.tag = tag
this.text = text
this.children = children
}

render() {
if(this.tag === '#text') {
return document.createTextNode(this.text)
}
let el = document.createElement(this.tag)
this.children.forEach(vChild => {
el.appendChild(vChild.render())
})
return el
}
}

function v(tag, children, text) {
if(typeof children === 'string') {
text = children
children = []
}
return new VNode(tag, children, text)
}

这里定义一个简单虚拟node类,拥有本元素和子节点,拥有render()函数。

改变使用diff算法对比

1
2
3
4
5
6
7
8
9
10
11
12
13
function patchElement(parent, newVNode, oldVNode, index = 0) {
if(!oldVNode) {
parent.appendChild(newVNode.render())
} else if(!newVNode) {
parent.removeChild(parent.childNodes[index])
} else if(newVNode.tag !== oldVNode.tag || newVNode.text !== oldVNode.text) {
parent.replaceChild(newVNode.render(), parent.childNodes[index])
} else {
for(let i = 0; i < newVNode.children.length || i < oldVNode.children.length; i++) {
patchElement(parent.childNodes[index], newVNode.children[i], oldVNode.children[i], i)
}
}
}

实际远远比这个复杂,拥有更多细节要处理,不过理解的话够用了。