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

2023年2月3日

💎 加入 E+ 成長計畫 與超過 350+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

回流 (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 等。

何时发生重绘 (Repaint)?

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

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

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

以下提供几个方法

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