组件如何渲染
初次渲染过程
- 解析模版为
render
函数(或在开发环境已经完成,vue-loader) - 触发响应式,监听
data
属性getter
setter
- 执行
render
函数,生成vnode
,patch(elem, vnode)
更新过程
- 修改
data
, 触发setter
(此前在getter
中已被监听) - 重新执行
render
函数,生成newVnode
patch(vnode, newVnode)
异步渲染 $nextTick
- 汇总
data
的修改,一次性更新视图 - 减少 DOM 操作次数,提高性能
虚拟 DOM 和 diff
- vdom 是实现 vue 和 React 的重要基石
- diff 算法是vdom 中最核心、最关键的部分
用JS模拟DOM结构
1
2
3
4
5
6
<div id="div1" class="container">
<p>vdoom</p>
<ul style="font-size:20px">
<li>a</li>
</ul>
</div>
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
{
tag: 'div',
props: {
className: 'container',
id: 'div1'
},
children: [
{
tag: 'p',
children 'vdom'
},
{
tag: 'ul',
props: { style: 'font-size: 20px' },
children: [
{
tag: 'li',
children: 'a'
}
// ....
]
}
]
}
下面用js将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
function parseHTMLToVNode(htmlString) {
// 创建一个临时的DOM元素
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlString.trim();
// 递归解析节点
function parseNode(node) {
if (node.nodeType === 3) { // 文本节点
return node.nodeValue;
}
if (node.nodeType === 1) { // 元素节点
const vnode = {
tag: node.tagName.toLowerCase(),
attrs: {},
children: []
};
// 解析属性
for (let attr of node.attributes) {
vnode.attrs[attr.name] = attr.value;
}
// 递归解析子节点
for (let childNode of node.childNodes) {
vnode.children.push(parseNode(childNode));
}
return vnode;
}
}
return parseNode(tempDiv.firstChild);
}
// 测试
const htmlString = '<div id="text" class="content">这是一段内容<span style="color: red">我是标会的内容</span>不知道是什么内容<p>1234567</p>哈哈哈哈</div>';
const vdom = parseHTMLToVNode(htmlString);
console.log(vdom);
// {"tag":"div","attrs":{"id":"text","class":"content"},"children":["这是一段内容",{"tag":"span","attrs":{"style":"color: red"},"children":["我是标会的内容"]},"不知道是什么内容",{"tag":"p","attrs":{},"children":["1234567"]},"哈哈哈哈"]}
通过 snabbdom学习 vdom
- 简洁强大的vdom库,易学易用(https://github.com/snabbdom/snabbdom)
- vue 参考它实现的 vdom 和 diff
Vue2 Vue3 React 三者 diff 算法有何区别?
vue2
- 双端比较vue3
- 最长递增子序列react
- 仅右移