彻底理解前端工程优化难点之重绘、回流和图层
重绘、回流和图层
在前面的学习中,我们知道浏览器在渲染一个页面的过程是这样的:
-
解析HTML构建DOM树
渲染引擎使用HTML解析器(调用XML解析器)解析HTML(XML)文档,将各个HTML(XML)元素逐个转化成DOM节点,从而生成DOM树。
接着渲染引擎使用CSS解析器解析外部CSS文件及style标签中的样式信息,构建出CSSOM 树。
-
构建渲染树
渲染引擎使用上一步CSS解析器解析得到的样式规则,将其附着到DOM树上,从而构成渲染树,其实就是讲DOM树和CSSOM树合并成渲染树。
-
布局渲染树
渲染树构建完毕之后,进入布局阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。
-
绘制render树
渲染引擎将遍历渲染树,并调用显示后端将每个节点绘制出来。
在整个页面被渲染的过程中,最后两个步骤是: layout
(布局) 和 paint
(绘制)。
重绘
其实就是触发浏览器的 paint
的过程。
只触发重绘的样式:
样式属性名 | 样式属性名 | 样式属性名 |
---|---|---|
color | background | outline-color |
border-style | background-image | outline |
border-radius | background-position | outline-style |
visibility | background-repeat | outline-width |
text-decoration | background-size | box-shadow |
回流
其实不仅仅是触发了 paint
的过程还触发 layout
的过程,所以回流必定触发重绘。
能触发回流的样式:
样式属性名 | 样式属性名 | 样式属性名 |
---|---|---|
width | top | text-align |
height | bottom | overflow-y |
padding | left | font-weight |
margin | right | overflow |
display | position | font-family |
border-width | float | line-height |
border | clear | vertival-align |
min-height | font-size | white-space |
能触发回流的JS
操作
JS 属性或者方法 |
JS 属性或者方法 |
JS 属性或者方法 |
JS 属性或者方法 |
JS 属性或者方法 |
---|---|---|---|---|
offsetTop | scrollTop | clientTop | width | getComputedStyle() |
offsetLeft | scrollLeft | clientLeft | height | IE的:currentStyle |
offsetWidth | scrollWidth | clientWidth | getBoundingClientRect() | |
offsetHeight | scrollHeight | clientHeight |
另外,只要是获取元素位置相关的操作,都会触发回流。
可以避免重绘和回流的方式是启用CSS的硬件加速,硬件加速其实就是GPU加速,可以触发硬件加速的css属性有:
样式属性名 | 样式属性名 | 样式属性名 | 样式属性名 |
---|---|---|---|
transform | opacity | filters | Will-change |
图层(Layer)
浏览器的渲染策略是分层的,当遇到会增加图层的标签的时候,浏览器会生成一个新的图层(比如:div、canvas等),之后该元素内的重绘和回流的过程只存在于该图层中。之后再进行图层的合并,这样重绘和回流的效率就会提高,但是会增加一个图层的合并过程。
优化思路
明白了重绘和回流等知识,我们的优化思路其实就有了,其实就是:减少重绘和回流,因此我们以下几个地方入手:
- 在布局上可以使用:translate替代top/left…、opacity替代visibility
- 操作DOM的时候
- 不要一条一条地修改 DOM 的样式,预先定义好 class,然后修改 DOM 的 className,可以减少重绘和回流的次数
- 不要把 DOM 结点的属性值放在一个循环里当成循环里的变量,这将会导致页面一直处于回流的过程
- 不要使用
table
布局,因为你改动一点点都将影响整个table
的布局 - 对于动画,放到新建的图层中进行,这样重绘和回流就都只在该图层中进行
- 尽量少用
JS
去操作布局