回流 (Reflow) 和重绘 (Repaint) 是什么?以及如何优化?

2024年8月30日

💎 加入 E+ 成長計畫 如果你喜歡我們的內容,歡迎加入 E+,獲得更多深入的軟體前後端內容

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

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

浏览器渲染过程

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

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

    DOM and CSSOM 合并成为渲染树
    DOM and CSSOM 合并成为渲染树
    圖片來源:https://web.dev/critical-rendering-path-render-tree-construction/
  2. 布局 (Layout)

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

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

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

    如下图所示

    产生绘制纪录
    产生绘制纪录
    圖片來源:https://developer.chrome.com/blog/inside-browser-part3/#paint
  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)。

以下提供几个方法:

  • 移动调整元素时,使用  transform
  • 使用  opacity  来改变元素的能见度
  • 如果需要频繁重绘或回流的节点,可以透过  will-change  设定成独立的图层,因为独立的图层可以避免该节点渲染行为影像到其他节点。
    body > .sidebar {
      will-change: transform;
    }
    
  • 避免频繁用 JavaScript 操作 DOM 节点
🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們