回流 (Reflow) 是指网页渲染引擎根据元素的尺寸、位置和显示属性来重新计算页面的排版和布局,是网页渲染过程中的一个重要步骤。重绘 (Repaint) 是指网页渲染引擎根据显示属性 (如颜色、文字大小等) 重新绘制页面元素,不影响元素的位置和尺寸。

通过有效控制回流和重绘,可以提高网页的渲染性能。本篇文章会从浏览器渲染过程,讨论到回流 (Reflow) 和重绘 (Repaint) 的概念,以及如何优化。

浏览器渲染过程

浏览器在渲染画面时,会经过几个过程:

  1. 解析 HTML 和样式计算 (parsing and style calculation) 把 HTML 解析成 DOM,把 CSS 解析成 CSSOM,DOM 和 CSSOM 合并成渲染树 (render tree)。可参考下图所示:

  2. 布局 (Layout)

    渲染树 (render Tree)  有 DOM 的结构和每个节点的样式,但这还不足以呈现页面,还需要计算个节点在画面上的大小和位置,这个过程称之为布局 (layout),并且这个过程会产生一个布局树 (layout Tree)

  3. 绘制 (paint) 拥有 DOM、样式和布局仍然不足以呈现页面,浏览器仍然必须判断元素的绘制顺序。可以把这个过程想像成为绘画过程的注释 (paint record),例如:

    1. 首先是要画个背景
    2. 然后在 (x,y,w,h) 位置上是文字
    3. 然后再画个矩形

    如下图所示

  4. 合成 (compositing)

    前三个步骤中,浏览器已经获得了渲染页面所需的资讯,但为了提高整体渲染效率,浏览器会再透过合成 (compositing),将资讯渲染到画面上。合成 (compositing) 是一种将页面的各个部分分成图层 (layers) 的技术,而这个技术会在合成线程 (compositor thread)  这个单独的线程执行。在这个过程完成之后,还会再产生一个图层树 (layer tree),最终才会渲染到画面上。

回流 (Reflow) 和重绘 (Repaint)

了解完浏览器的渲染过程后,我们回到问题:浏览器中的回流 (Reflow) 和重绘 (Repaint) 是什么 ?以及如何优化?

回流 (Reflow) 和重绘 (Repaint)  指的就是渲染的布局 (layout)  和绘制 (paint)  的步骤。当我们做了某些事情改变布局或样式,就会触发回流 (Reflow) 或重绘 (Repaint)。

要注意的是,浏览器的渲染过程其实是有代价的,因为在渲染过程中,每个步骤都会使用上一个操作的结果来创建新数据。例如:如果布局树 (layout Tree)  改变,那就会需要重新绘制。所以如果能够尽量避免回流 (Reflow) 或重绘 (Repaint),就能够大大提升效能。

何时发生回流 (Reflow) 和重绘 (Repaint)

何时发生回流 (Reflow)?

影响浏览器效能很重要的关键因素,因为可能导致整个或部分页面的布局更新,可能因为一个节点大小的改变,就会触发整的页面的回流。例如:改变  widthheightfont-size  等。

当一个元素的长与宽改变,可能会影响到画面中其他元素的编排,所以每当有一个元素的布局改变,浏览器的 CPU 需要重新计算整个页面中不同元素的长宽、间距等。在计算的期间,会没办法处理起他任务 (例如使用者在点击按钮,可能会要等一阵子才有回应,因为浏览器无暇处理)。

这也是为什么,回流对效能的影响,往往比重绘来得大。

何时发生重绘 (Repaint)?

当页面上的某个元素需要改变颜色或其他不影响布局的属性时,浏览器会对其进行重绘 (repaint)。与回流不同,重绘不会影响页面布局,但是也会影响页面的性能。例如:改变  outlinevisibilitycolorbackground-color 等。

减少回流 (Reflow) 和重绘 (Repaint)

在浏览器渲染过程中最后一步骤是合成 (compositing),在某些情况,我们可以透过一些技巧只需要让浏览器合成 (compositing),而避免回流 (Reflow) 和重绘 (Repaint)。

以下提供几个方法:

技术手段原因说明
批量操作 DOM避免多次触发 reflow,使用 DocumentFragmentdisplay: none 批量修改
使用 class 替代 style 多次修改改 class 一次触发一次重排,style 连续改多次可能连续触发重排
避免频繁读写 layout 属性混用如:连续读取 offsetTop 然后设置 style.top,中间会插入 layout 计算流程
使用 transform / opacity 替代 top / lefttransform 不会触发重排,只会触发合成层变化(GPU 加速)
使用虚拟列表减少页面中渲染节点数量
使用 will-change 属性告诉浏览器元素将来的变化
body >.sidebar {  
	will-change: transform;  
}