3分鐘 2023-01-31

初步認識 Reflow 回流和 Repaint 重繪

簡介

現今比較容易影響前端效能大多是 JavaScript ,操作大量 DOM 確實會給瀏覽器較大的負擔。而變更 CSS 也有造成效能的兩大元凶:重排(回流)Reflow)和 重繪Repaint),今天就來介紹一下這兩個名詞吧。

瀏覽器如何渲染畫面?

在開始介紹兩個名詞之前,要先了解瀏覽器如何渲染畫面的,才能知道這兩個名詞分別在渲染的哪個步驟以及他們的工作。

  1. 從 HTML 解析出 DOM 樹
  2. 從 CSS 解析出 CSSOM 樹
  3. 兩者組合出 Render TREE
  4. 計算出 Render 樹每個元素的位置、大小,以及每個節點畫面上的座標
  5. 實際將結果繪製到畫面上

DOM 樹雖然和 Render 樹很相似,但 Render 知道樣式的設定,例如把一個 div 的 display 屬性設定為 none,這個 div 在 Render 樹就不會出現。

重排(回流)(Reflow)

Reflow 會根據 Render 樹逐步計算每個元素的位置座標、大小,以及其是否顯示的屬性等,與這類屬性有關的物件操作都會觸發 Reflow,以下有一些簡單的舉例:

  1. 頁面首次被載入的時候
  2. 進行 CSS 屬性設定,如:大小(height、width)、定位與浮動(position、float),或是設定偽類別(:hover)等
  3. 使用者互動,如:調整視窗大小、按鈕的狀態變動等
  4. JavaScript 進行 DOM 操作、取得元素大小或是動態載入CSS等。

由於 Reflow 相當耗費運算資源,所以瀏覽器會採用批次處理,不會馬上就處理 Reflow 需求,而是放到列隊中,需要時(每一個 frame)才會批次執行並清空列隊。

 重繪Repaint

Repaint 就是將經過 Reflow 計算後的結果轉換成螢幕上的實際像素,Repaint 會將可見元素的樣式變更(例如:元素的可見性、背景)重新繪製到畫面上,由於最後可見元素的變更都必須重新繪製到畫面上,Repaint 也就難以避免。

不要讓 Reflow 成為效能瓶頸

雖然 Reflow 在瀏覽器會被批次執行,但還是可以用一些方法來避免 Reflow,例如:

  • 少變更或使用定位屬性,如不用 top 而改用 translate,top 之類的定位屬性是真實改變元素位置,而 translate 只是相較於原位置產生偏移
  • 以 visibility: hidden 替換 display:none,由於 visibility 佔用位置,隱藏並不會產生 Reflow,而 display 不會佔用位置,隱藏則會產生 Reflow。
  • 不要使用表格 table 排版,由於 Reflow 時有可能會連同子元素一起而導致整張表 Reflow。 
  • 避免過深的 DOM 樹、CSSOM 樹
  • 避免過度頻繁更新樣式,如果要變更建議寫成 CSS 當中的 Class 一次完成變更。

如果上網路找文章,還能發現其他更多的方式,這裡就只列出我比較能理解的方式。

挑戰跨足工程師與設計師的反骨少年:D