回流 (Reflow) 和重绘 (Repaint) 是什么?以及如何优化?
2024年8月30日
回流 (Reflow) 是指网页渲染引擎根据元素的尺寸、位置和显示属性来重新计算页面的排版和布局,是网页渲染过程中的一个重要步骤。重绘 (Repaint) 是指网页渲染引擎根据显示属性 (如颜色、文字大小等) 重新绘制页面元素,不影响元素的位置和尺寸。
通过有效控制回流和重绘,可以提高网页的渲染性能。本篇文章会从浏览器渲染过程,讨论到回流 (Reflow) 和重绘 (Repaint) 的概念,以及如何优化。
浏览器渲染过程
浏览器在渲染画面时,会经过几个过程:
解析 HTML 和样式计算 (parsing and style calculation) 把 HTML 解析成 DOM,把 CSS 解析成 CSSOM,DOM 和 CSSOM 合并成渲染树 (render tree)。可参考下图所示:
布局 (Layout)
渲染树 (render Tree) 有 DOM 的结构和每个节点的样式,但这还不足以呈现页面,还需要计算个节点在画面上的大小和位置,这个过程称之为布局 (layout),并且这个过程会产生一个布局树 (layout Tree)。
绘制 (paint) 拥有 DOM、样式和布局仍然不足以呈现页面,浏览器仍然必须判断元素的绘制顺序。可以把这个过程想像成为绘画过程的注释 (paint record),例如:
- 首先是要画个背景
- 然后在 (x,y,w,h) 位置上是文字
- 然后再画个矩形
如下图所示
合成 (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)?
影响浏览器效能很重要的关键因素,因为可能导致整个或部分页面的布局更新,可能因为一个节点大小的改变,就会触发整的页面的回流。例如:改变 width
、height
、font-size
等。
当一个元素的长与宽改变,可能会影响到画面中其他元素的编排,所以每当有一个元素的布局改变,浏览器的 CPU 需要重新计算整个页面中不同元素的长宽、间距等。在计算的期间,会没办法处理起他任务 (例如使用者在点击按钮,可能会要等一阵子才有回应,因为浏览器无暇处理)。
这也是为什么,回流对效能的影响,往往比重绘来得大。
何时发生重绘 (Repaint)?
当页面上的某个元素需要改变颜色或其他不影响布局的属性时,浏览器会对其进行重绘 (repaint)。与回流不同,重绘不会影响页面布局,但是也会影响页面的性能。例如:改变 outline
、visibility
、color
、background-color
等。
减少回流 (Reflow) 和重绘 (Repaint)
在浏览器渲染过程中最后一步骤是合成 (compositing),在某些情况,我们可以透过一些技巧只需要让浏览器合成 (compositing),而避免回流 (Reflow) 和重绘 (Repaint)。
以下提供几个方法:
- 移动调整元素时,使用
transform
- 使用
opacity
来改变元素的能见度 - 如果需要频繁重绘或回流的节点,可以透过
will-change
设定成独立的图层,因为独立的图层可以避免该节点渲染行为影像到其他节点。body > .sidebar { will-change: transform; }
- 避免频繁用 JavaScript 操作 DOM 节点