回流 (Reflow) 和重繪 (Repaint) 是什麼?以及如何優化?

2023年2月3日

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

回流 (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 上追蹤我們