4.1 Virtual DOM
4.1.1 浏览器的渲染流程
当浏览器接收到一个Html
文件时,JS引擎和浏览器的渲染引擎便开始工作了。从渲染引擎的角度,它首先会将html
文件解析成一个DOM
树,与此同时,浏览器将识别并加载CSS
样式,并和DOM
树一起合并为一个渲染树。有了渲染树后,渲染引擎将计算所有元素的位置信息,最后通过绘制,在屏幕上打印最终的内容。而JS
引擎的作用是通过DOM
相关的API
去操作DOM
对象,而当我们操作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'
}]
}