引言
React 使用的是 Virtual DOM,使得元素脱离了文档流,将 DOM 的比对转化为 JS 的比对。这种比对用到了虚拟 DOM 的 diff 算法
背景知识
重排(回流 Reflow)与重绘(Repaint)
重排(回流)是一整个浏览器变化的情况,需要重建 DOM 树
这种过程一般发生在:
- 首次渲染
- 窗口大小改变
- 部分元素大小位置变化
- 字体大小变化
- 增删可见 DOM 元素
- 激活 CSS 伪类
总的来说,要么是浏览器本身发生了大小变化,要么是其中的 DOM 元素大小变化影响到其他 DOM 元素了,这样文档流会发生变化。
关于为什么激活 CSS 伪类也会出现回流,我没有查到相关资料,欢迎大家留言
重绘则发生于一小块变化的情况,这种变化不影响其他部分的位置,比如:
- color
- background-color
- visibility
显然,重排比重绘代价高很多。
虚拟 DOM(Virtual DOM)
从 JSX 到浏览器 DOM
JSX -> createElement 方法 -> JS 对象(虚拟 DOM) -> 真实 DOM
JSX 中的标签事实上是一种语法糖,实质是调用 React.createElement()生成一个 JS 对象
如:
<div>Hello {this.props.toWhat}</div>
生成的是
React.createElement('div', null, `Hello ${this.props.toWhat}`);
原理
由于有了虚拟 DOM 的设定,元素脱离了文档流,不再直接操作 DOM,而是将多重修改合并,再将元素带回文档中避免频繁触发回流与重绘
虚拟 DOM 的 Diff 算法
全称是 difference 算法
同级比较,若某一层两个虚拟 DOM 节点不一致,就不再往下比较了,直接将原始页面的虚拟 DOM 删除掉,替换新的。
对于 list,每一个元素都有一个单独的 key 值(一般为后台数据的唯一 id 字段,不能是 index),根据 key 值来判断更新前后的 item 是不是一样的
例:
不使用 key
原始:
(a) (b) (c) (d) (e) (f)
插入(g)到(b)(c)之间:
(a) (b) (g) (c) (d) (e) (f)
需要更新的DOM节点有:5个
(a) (b) (c) (d) (e) (f)
(a) (b) (g) (c) (d) (e) (f)
√ √ × × × × ×
使用 key
原始:
(a:1) (b:2) (c:3) (d:4) (e:5) (f:6)
插入(g:7)到(b:2)(c:3)之间:
(a:1) (b:2) (g:7) (c:3) (d:4) (e:5) (f:6)
需要更新的DOM节点有:1个
(a:1) (b:2) (c:3) (d:4) (e:5) (f:6)
(a:1) (b:2) (g:7) (c:3) (d:4) (e:5) (f:6)
√ √ × √ √ √ √
如果使用 index 作为 key:
原始:
(a:1) (b:2) (c:3) (d:4) (e:5) (f:6)
插入(g)到(b)(c)之间:
(a:1) (b:2) (g:3) (c:4) (d:5) (e:6) (f:7)
需要更新的DOM节点有:5个
(a:1) (b:2) (c:3) (d:4) (e:5) (f:6)
(a:1) (b:2) (g:3) (c:4) (d:5) (e:6) (f:7)
√ √ × × × × ×