盒子
盒子
文章目录
  1. 基本的代码如下:
  2. 可以在HTML中直接测试

虚拟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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
let nodeData = {
tag: 'div',
children: [{
tag: 'p',
children: [{
tag: 'span',
children: [{
tag: '#text',
text: 'wushao.xyz'
}]
}]
},
{
tag: 'span',
children: [{
tag: '#text',
text: 'wushao.com'
}]
}
]
}

/*
等同于这段html代码
<div>
<p>
<span>wushao.xyz</span>
</p>
<span>wushao.com</span>
</div>
*/

/**
* ES6的写法
*/
class VNode {
constructor(tag, children, text) {
this.tag = tag
this.children = children
this.text = text
}

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
}
}

/**
* 创建虚拟dom节点的简便函数
* @param {标签名} tag
* @param {子标签} children
* @param {文本值} text
*/
function v(tag, children, text) {
//如果没有tag,第二个参数是字符串的话
if (typeof children === 'string') {
text = children
children = []
}
return new VNode(tag, children, text)
}

/**
* ES5的写法
* @param {*} tag
* @param {*} children
* @param {*} text
*/
function vNode(tag, children, text) {
this.tag = tag
this.children = children
this.text = text
}
vNode.prototype.render = function () {
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
}

//举例说明
let vNode1 = v('div', [
v('p', [
v('span', [v('#text', 'wushao.xyz')])
]),
v('span', [
v('#text', 'wushao.com')
])
])
console.log(vNode1);

const root = document.querySelector('#root')
root.appendChild(vNode1.render()) //把虚拟的dom映射进了真实的dom结构里面

let vNode2 = v('div', [
v('p', [
v('span', [v('#text', 'wushao.xyz')])
]),
v('span', [
v('#text', 'wushao.com')
]),
v('span', [
v('#text', 'wushao')
])
])

document.querySelector('#change').onclick = function () {
root.innerHTML = ''
root.appendChild(vNode2.render())
}

/**
* 比较前后两个节点
* @param {要比较的DOM结构} parent
* @param {旧的节点} newVNode
* @param {*新的节点 oldNode
* @param {索引} index
*/
function patchElement(parent, newVNode, oldNode, index = 0) {
if (!oldNode) {
//新增元素
parent.appendChild(newVNode.render())
} else if (!newVNode) {
parent.removeChild(parent.childNodes[index])
} else if (newVNode.tag !== oldNode.tag || newVNode.text != oldNode.text){
parent.replaceChild(newVNode.render(), parent.childNodes[index])
} else {
for (let i = 0; i < newVNode.children.length || i < oldNode.children.length; i++) {
patchElement(parent.childNodes[index], newVNode.children[i], oldNode.children[i], i)
}
}

}

可以在HTML中直接测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>虚拟DOM</title>
</head>
<body>
<div id="root">

</div>
<button id="change">change</button>
<script src="./v-dom.js"></script>
</body>
</html>