認識Reflow和Repaint

Joe Chang
Coding Hot Pot
Published in
Jul 18, 2021
photo by @steve_j

效能問題對於開發者來說一直都是一項難題,畢竟無法確保每個使用者的裝置效能都有中上水平,特別是某些中低階的手機,所以就必須關注那些行為可能會是效能殺手,導致使用者無法流暢的瀏覽網頁,為了避免自己不小心埋下效能的地雷, 就需要來了解瀏覽器的兩個機制 Reflow和Repaint

在討論之前Reflow和Repaint之前,先來理解瀏覽器的渲染過程吧!

圖片來源 https://gist.github.com/faressoft

1.解析HTML檔案,生成DOM tree

2. 解析CSS檔案,生成CSSOM

3. 將DOM與CSSOM合併為Render Tree

(如果遇到display:none的元素 ,則不會被計算進去)

4. 計算每個可見元素的佈局(寬高和位置)

5. 將Render Tree計算結果繪製到畫面上

而Reflow和Repaint則會在第四和第五個步驟觸發

Reflow(回流)

當Render Tree的佈局改變,就會重新計算DOM的位置和大小,這個過程稱之為Reflow,舉例來說 , 在頁面載入完畢之後 ,刪除了某個DOM節點 ,這時候瀏覽器就必須重新計算佈局 。

頁面第一次載入的時候,一定會觸發一次Reflow

甚麼情況下會造成Reflow ?

  • Window Resizing(變更視窗尺寸)
  • 對DOM元素進行新增、 修改 、移除的操作
  • 更改CSS的樣式(會影響佈局的): padding、 margin、 border、 width、 font-family、 font-size
  • 新增或移除DOM的樣式
  • 修改input的內容
  • 獲取元素的特定屬性(scrollTop、scrollLeft、scrollWidth、scrollHeight、 offsetTop、offsetLeft、offsetWidth、offsetHeight、clientTop、clientLeft、clientWidth、clientHeight)

瀏覽器在處理Reflow這件事有個優化機制 ,會將多個Reflow和Repaint放到佇列,然後在每一個requestAnimationFrame(每 16.6ms)清空佇列,合併為一次的處理然後更新畫面,但如果當你需要獲取offsetTop 、offsetWidth、 scrollTop、 clientTop…這些屬性或是呼叫scrollIntoView() 方法的時候 ,瀏覽器為了確保你拿到的是最新的值,就必須馬上觸發Reflow 。

Reflow必定會觸發Repaint

Repaint(重繪)

Render Tree的樣式改變 ,單純改變外觀顏色,不影響佈局,稱之為Repaint,相較於Reflow,Repaint的效能開銷就小很多。

甚麼情況下會造成Repaint?

  • 修改DOM元素的CSS屬性:background-color 、color、 opacity、 visibility

簡單來說:

需要重新計算DOM節點 → Reflow

更改樣式重新繪製畫面,不涉及畫面排版→ Repaint

如何減少Reflow的次數?

  • 避免用table排版
  • 如果要對該DOM元素設定動畫,可以先設定postion為absolute或是fixed,讓該元素脫離文件流,就不會影響到其他元素的佈局
  • 如果要用JS來設定樣式的話,避免逐行修改,改用ClassName來修改樣式
// bad
element.style.left = left + "px";
element.style.top = top + "px";
//good
element.className += "newStyle";
  • 使用DocumentFragment來暫存對DOM的變更,最後再一次apply變更到DOM上 ,這樣就可以將多次的Reflow和Repaint降低為一次
  • CSS的層級避免寫得太深,因為瀏覽器需要花費更多效能去找出符合規則的元素
  • 犧牲動畫的精緻度, Ex 每次移動1px v.s 每次移動3px ,後者雖然動畫效果較不流暢但效能會比較好
  • 避免多次讀取offsetTop、 clientTop等屬性 ,可以用一個變數將值存起來
  • 用visibility 代替 display:none

在網路上看到一個很有趣的比喻 : DOM整形就會觸發Reflow,DOM化妝就會觸發Repaint。

參考資料:DOM Performance

--

--

Joe Chang
Coding Hot Pot

前端工程師,唯有非常努力,才能看起來毫不費力