最近在重看一些基础的理论知识,看到了回流(Reflow)和重绘(Repaint)的概念,惭愧的发现,对此竟然说不出太详细的内容,在此参阅一些文章进行一个总结。
在了解这两个概念之前先了解一下 HTML 的渲染过程。
HTML 渲染过程
浏览的引擎处理 HTML 的基本流程分为如下四个步骤:
1.解析 HTML 并构建 DOM 树和 CSSOM 树。浏览器对 HTML 进行解析,将 HTML 标记转换成文档对象模型 (DOM),CSS 标记则被转换成 CSS 对象模型 (CSSOM),而 DOM 和 CSSOM 是独立的数据结构。在解析过程中,DOM 树包含了所有的 html 标签,包括不展示的 head 节点和 display:none 的节点,而 CSSOM 树则会去掉浏览器不能识别的样式,比如不支持的浏览器前缀(chrome不支持的-moz-前缀)和 hack(如firefox不支持_开头的样式)。
2.构建 render 树。将 DOM 树和 CSSOM 树合并为 render 树,在这个过程中,需要计算每一个呈现对象的可视化属性,会去掉不展示在页面上的节点(如 display:none 和 head 节点等)。
3.布局 render 树。render 树在创建完成时,并不包含位置和大小信息。计算这些值的过程称为布局或重排。从 render 树的根节点((对应于 HTML 文档的 元素))递归调用,计算每一个元素的位置和大小信息。
4.使用 render 树绘制页面。在绘制阶段,系统会遍历 render 树,将其内容显示在屏幕上。绘制的顺序其实就是元素进入堆栈样式上下文的顺序。这些堆栈会从后往前绘制,因此这样的顺序会影响绘制。
这就是浏览器引擎处理页面的基本流程了。接下来我们看一下回流和重绘的概念
回流(Reflow)和重绘(Repaint)
回流和重绘分别出现在上面的第三和第四步。
- 回流(Reflow):当 render 树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。
- 重绘(Repaint):当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响 render 树重新布局的,比如修改了元素的 background-color。
由上定义可以看出,回流必将引起重绘,而重绘不一定会引起回流。明显回流的成本比重绘的成本高得多。
引起回流和重绘的操作
任何对 render 树中元素的操作都会引起回流或者重绘:
- 改变 DOM 树结构,比如添加或者删除可见的元素、改变文本内容、改变位置;
- 改变元素几何尺寸:边距、填充、边框、宽度和高度;
- 用户改变浏览器窗口尺寸;
- CSS伪类激活,在用户交互过程中发生;
- 获取某些属性,浏览器一般对引起回流、重绘的操作进行优化,将多个操作合并在固定时候执行,如果此时获取一些 style 信息,则会提前执行操作,以提供准确数值。常见的这类信息如:
offsetTop/offsetLeft/offsetWidth/offsetHeight
、scrollTop/Left/Width/Height
,具体可以查看 CSS Triggers
减少回流和重绘
减少回流、重绘其实就是需要减少对render 树的操作,合并多次 DOM 和样式的修改,并减少对一些style信息的请求。具体有:
避免多次更改样式属性,合并为一个
当更该样式的时候,可以更改class 或者将多个更改操作合并为一个:
// 不好的写法 |
将要操作的元素“离线处理”
主要注意三点:
- 使用 DocumentFragment 进行缓存操作,引发一次回流和重绘;
- 使用 display:none 技术,只引发两次回流和重绘;
- 使用 cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;
减少操作影响的元素
将导致多次回流的元素,position 属性设为 absolute 或 fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。
读取引起回流的属性,尽量缓存
// 不好的写法 |