4.1 Virtual DOM
4.1.1 浏览器的渲染流程
当浏览器接收到一个Html
文件时,JS
引擎和浏览器的渲染引擎便开始工作了。从渲染引擎的角度,它首先会将html
文件解析成一个DOM
树,与此同时,浏览器将识别并加载CSS
样式,并和DOM
树一起合并为一个渲染树。有了渲染树后,渲染引擎将计算所有元素的位置信息,最后通过绘制,在屏幕上打印最终的内容。JS
引擎和渲染引擎虽然是两个独立的线程,但是JS引擎却可以触发渲染引擎工作,当我们通过脚本去修改元素位置或外观时,JS
引擎会利用DOM
相关的API
方法去操作DOM
对象,此时渲染引擎变开始工作,渲染引擎会触发回流或者重绘。下面是回流重绘的两个概念:
- 回流: 当我们对
DOM
的修改引发了元素尺寸的变化时,浏览器需要重新计算元素的大小和位置,最后将重新计算的结果绘制出来,这个过程称为回流。 - 重绘: 当我们对
DOM
的修改只单纯改变元素的颜色时,浏览器此时并不需要重新计算元素的大小和位置,而只要重新绘制新样式。这个过程称为重绘。
很显然回流比重绘更加耗费性能。
通过了解浏览器基本的渲染机制,我们很容易联想到当不断的通过JS
修改DOM
时,不经意间会触发到渲染引擎的回流或者重绘,这个性能开销是非常巨大的。因此为了降低开销,我们需要做的是尽可能减少DOM
操作。有什么方法可以做到呢?
4.1.2 缓冲层-虚拟DOM
虚拟DOM
是为了解决频繁操作DOM
引发性能问题的产物。虚拟DOM
(下面称为Virtual DOM
)是将页面的状态抽象为JS
对象的形式,本质上是JS
和真实DOM
的中间层,当我们想用JS
脚本大批量进行DOM
操作时,会优先作用于Virtual DOM
这个JS
对象,最后通过对比将要改动的部分通知并更新到真实的DOM
。尽管最终还是操作真实的DOM
,但Virtual DOM
可以将多个改动合并成一个批量的操作,从而减少 DOM
重排的次数,进而缩短了生成渲染树和绘制所花的时间。
我们看一个真实的DOM
包含了什么:
浏览器将一个真实DOM
设计得很复杂,不仅包含了自身的属性描述,大小位置等定义,也囊括了DOM
拥有的浏览器事件等。正因为如此复杂的结构,我们频繁去操作DOM
或多或少会带来浏览器的性能问题。而作为数据和真实DOM
之间的一层缓冲,Virtual DOM
只是用来映射到真实DOM
的渲染,因此不需要包含操作 DOM
的方法,它只要在对象中重点关注几个属性即可。
// 真实DOM
<div id="real"><span>dom</span></div>
// 真实DOM对应的JS对象
{
tag: 'div',
data: {
id: 'real'
},
children: [{
tag: 'span',
children: 'dom'
}]
}