前端三十|03. [CSS] Reflow 及 Repaint 是什麼?

昨天 聊了 HTML 控制 JavaScript 載入順序的方法,設定得宜可以提升不少效能;那麼今天就來聊聊可能造成 CSS 影響效能的其中兩位兇手:回流(Reflow)& 重繪(Repaint)。
但在進入正題之前,我們要先認識一下瀏覽器的渲染機制。
本系列文已經重新編校彙整編輯成冊,並正式出版囉!
《前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法》好評販售中!
喜歡我文章內容的讀者們,歡迎您前往購買支持!
瀏覽器的渲染步驟
上圖是瀏覽器解析網頁的示意圖,為方便理解,我們就簡單拆解成幾個步驟:
- 從 HTML 檔解析出 DOM Tree
- 從 CSS 檔解析出 CSSOM Tree
- 兩者疊加後產生 Render Tree
- Reflow:計算出 Render Tree 上各個元素的物理屬性,如位置、大小、及是否看得見(visible)
- Repaint:將計算結果轉為實際的像素,畫到畫面上
Reflow
如同前述,這個步驟會由 Render Tree 的根結點出發,逐步計算出每一個元素的位置、大小,以及是否被其他元素遮擋等屬性,需要耗費大量的運算資源;也因為是要計算出這些屬性,只要是有可能牽扯到這些屬性的操作,都會觸發 Reflow,例如:
1. 設定 CSS 屬性
- 大小:
width、height - 浮動:
float - 定位:
position - …
2. 使用者進行互動
- 調整瀏覽器視窗大小
- 輸入框的內容變更
3. JavaScript
- DOM 操作
- 動態載入 CSS 樣式表
- 取得元素的大小數值
比較需要注意的是「取得元素的大小數值」這一項,由於 Reflow 的計算相對於其他步驟需要較多運算效能,當有 Reflow 的需求時,瀏覽器不會馬上執行,而是會將它放到內部的等待隊列中,當需要時(每一 frame)才批次執行,並清空隊列。
這裡的 frame 也就是 window.requestAnimationFrame() 的那個 幀數
考慮到 Reflow 批次執行的特性,當開發者要取得元素的物理屬性例如 scrollTop 時,可能程式執行的當下有樣式修改仍在等待隊列,尚未 Reflow 到畫面上;為了避免這樣的狀況,每當開發者要獲取元素大小數值時,瀏覽器便會強制觸發一次 Reflow、以確保程式能取到正確的位置。而這也是許多網站的滑鼠滾輪事件監聽沒寫好,就讓整個網站超卡的原因!
Repaint
經過了 Reflow 的計算,Repaint 的任務是要把計算結果轉換成螢幕上的實際像素顯示。相比於 Reflow ,Repaint 就單純多了,任何可見元素的樣式變更,最後都必然需要重新繪製到畫面上,這是難以避免的效能開銷。
更詳細的 CSS 屬性觸發對照表,可以參考 CSS Triggers
拯救你的網頁效能
瀏覽器開發者也知道這些渲染步驟很吃效能,所以前輩們在實作時便早已寫好了優化規則:
- 如同 Reflow 段落提到的,由於 Reflow 極耗效能,瀏覽器會 自動批次執行。
- 當 DOM 元素的樣式被修改觸發前述步驟時,瀏覽器會依據修改的屬性而 自動省略 不需要的步驟,重新渲染頁面。
修改
width→ Reflow → Repaint
修改
color→ Repaint
理解上述規則之後,讀者您應該就能猜到該如何優化 CSS 效能了吧?,以下也提供幾種常見的方法:
屬性替換
將物理屬性的變化,換成相似的其他屬性變化,來節省 Reflow 的效能開銷:
- 用
translate取代top等定位屬性 - 由於表格的物理屬性會互相影響,容易改一格就整張表 Reflow,可以的話請不要用 table 排版

批次修改
當需要用 JavaScript 修改樣式時,盡量讓樣式能批次生效:
- 替換 class name 或修改
cssText,而不是逐個設定 style 屬性 - 透過
el.cloneNode()複製一份 DOM,在上面修改樣式後,在替換原本的 DOM - 透過
document.createDocumentFragment()建立 Document Fragments,編輯 DOM 後再加回主 DOM Tree 中
減少影響範圍
如果 Reflow 是避免不了的,那就只能減少影響範圍了:
- 盡量避免 DOM、CSSOM Tree 的層級過深,加快 Reflow 的計算
- 程式取得元素物理屬性時,將結果暫存起來,不要重複觸發計算
- 改動頻繁的地方 建立單獨的圖層(stacking context)
結語
以上就是這次的回流與重繪的說明,之後在撰寫 CSS 時,記得要考慮到對畫面渲染效能的影響喔!如果對於內文有疑問或不清楚的地方,都歡迎您在底下留言回應一起討論;明天將接續今天的 CSS 主題,敬請期待!
參考資料
- MDN — Document.createDocumentFragment()
- MDN — The stacking context
- CSS Triggers
- faressoft: DOM Performance
- 掘金 — reflow和repaint引发的性能问题
本文同步發表於 2020 IT 邦幫忙鐵人賽






