试着去理解虚拟DOM
去掉虚拟两个字,先了解下什么是DOM?
在原生JS中有DOM操作,就是使用浏览器提供的一些API,选中一个元素对它进行操作,比如说:增加一个属性、增加一个孩子、修改它里面的内容、改变它的位置,直接对DOM操作。这个DOM实际上对应的就是浏览器里面你所能看的见的对应的某个元素。
那什么是虚拟DOM
假设我们要操作一个数据的报表,这个报表大概有几百条数据,我们要对这个报表进行一个排序的操作
我们有这样一个表格,这里面大概有100多项
姓名 | 年纪 | 分数 | 等等 |
---|---|---|---|
小红 | 12 | 44 | |
小花 | 13 | 78 |
当我去点年纪的时候,希望年纪里的所有DOM元素可以按照年纪去排序,当我点新增的时候会在表格下面再新增一行数据,当我点击姓名的时候所有DOM按照姓名去做一个排序,我们可以想下用原生JS怎么去做排序,用JS去对DOM结构去排序,操作起来很难实现,也很麻烦。
后来我们有了MVVM框架可以对数据排序,数据对应到页面上的DOM结构,我们只需要对数据排序,那里面的DOM结构自然就排序了。
我们自己实现一个框架,假设数据变了,那我们的DOM结构也就变了,难道我们需要把数据重新渲染一次,如果用户频繁大量改动数据,DOM也会频繁改动,就会造成卡顿,那我们可以去做一些优化,怎么去做优化呢?
那我们可以像计算机内存那样,我们可以自己设定一个虚拟的数据结构,它是对真实的DOM结构是一一对应的,我们可以先对虚拟的数据结构进行操作,等全部操作完成了,再把它渲染成真实的DOM,那就变成了真实的数据。
- 那这样做有什么好处呢?
假设用户只做了微小的改动,比如增加了2条数据,那我们可以想打补丁一样,只把这两条加到真实的DOM里,而不用把整个DOM重新渲染。
那这是虚拟DOM以及他的作用
那虚拟DOM就是针对真实DOM做的一个一一映射的类似虚拟的数据结构,有了变化再把数据结构渲染到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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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)
}
/*
//虚拟的JSON格式的数据结构
let nodesData = {
tag: 'div',
children: [
{
tag: 'p',
children: [
{
tag: 'span',
children: [
{
tag: '#text',
text: 'hi'
}
]
}
]
},
{
tag: 'span',
children: [
{
tag: '#text',
text: 'oo'
}
]
}
]
}
*/
let vNodes = v('div', [
v('p', [
v('span', [ v('#text', 'hi') ] )
]
),
v('span', [
v('#text', 'oo')
])
]
)
console.log(vNodes.render())
什么是diff
我们需要做一些改变的时候,比如增删改查,那么我们需要将改变后的虚拟DOM树与真实的DOM树做对比,找出差异,然后做到局部更新改变的地方,那么找出差异就是diff(算法找两棵DOM树的差异)。
以下代码为简单的实现 DOM diff
1 | function patchElement(parent, newVNode, oldVNode, index = 0) { |