關於Reflow和Repaint
紀錄關於Reflow和Repaint的特性以及如何優化網頁
在前端開發中,再經果多次的開發修改後,總會不可避免的會遇到卡頓問題,這時我們就要做性能優化。而在優化時,我們會首先考慮畫面的渲染,因為這對使用者來說是最有感的。
所以了解和控制瀏覽器的渲染過程是十分重要的。而在渲染中,Reflow和Repaint是其中兩個蠻重要的關鍵概念,理解它們有助我們編寫更好的代碼,從而提升網頁的性能和用戶體驗。
理解瀏覽器的渲染過程
在介紹reflow和repaint前,先簡單的說下渲染過程。
下圖是Webkit的渲染流程,大部分的瀏覽器渲染過程如下 (Reference: 瀏覽器的運作方式 | Articles | web.dev):
- a) 解析HTML檔案,生成DOM tree
- b) 解析CSS檔案,生成CSSOM
- 將DOM與CSSOM合併為Render Tree
- 計算每個可見元素的佈局,像是寬高和位置等等。
- 如果遇到display:none的元素,則不會被計算進去,但該元素是會被構建DOM Tree的
- 將Render Tree計算結果繪製到畫面上
而Reflow和Repaint則會在第三和第四個步驟觸發。
一、什麼是Reflow?
1.1 定義
Reflow,又稱回流,是指當DOM元素的幾何屬性(例如尺寸、位置)發生變化時,瀏覽器需要重新計算整個頁面的佈局。這是一個代價高昂的過程,因為它可能影響到整個頁面或頁面中的大量元素。
1.2 觸發Reflow的因素
- 添加或移除DOM元素
- 修改元素的尺寸或邊距
- 改變瀏覽器窗口的大小
- 應用CSS樣式的改變
- 使用JavaScript操作元素的樣式屬性
1.3 Reflow的影響
Reflow是一個同步的、不可避免的過程,並且是性能瓶頸之一。當頁面中有大量的Reflow時,會導致頁面卡頓,影響用戶體驗。
1.4 重點事項
- 頁面第一次載入時,一定會觸發一次Reflow
- Reflow必定會觸發Repaint
二、什麼是Repaint?
2.1 定義
Repaint,又稱重繪,是指當元素的外觀(例如顏色、背景)發生變化,但尺寸和位置不變時,瀏覽器需要重新繪製元素。Repaint的代價相對較低,因為它只涉及到渲染樹的某些部分。
2.2 觸發Repaint的因素
- 更改元素的背景色、邊框色
- 修改文字顏色
- 改變透明度
- 應用CSS樣式的改變
2.3 Repaint的影響
雖然Repaint比Reflow的開銷要小,但頻繁的Repaint仍然會影響頁面的渲染性能。特別是在移動設備上,Repaint的代價會更高,因為這些設備的處理能力有限。
三、如何優化Reflow和Repaint
3.1 優化Reflow的方法
-
避免逐個修改樣式 將多個樣式改變合併為一次性修改。例如,使用
class
替代多次修改單個樣式屬性。 -
批量操作DOM 使用
documentFragment
或cloneNode
等方法一次性對多個元素進行操作,然後再將結果插入到DOM中。 -
減少使用浮動布局 浮動元素會影響其後所有元素的布局,應儘量避免頻繁使用浮動布局,改用Flexbox或Grid布局。
-
避免觸發同步布局 使用
offsetWidth
、offsetHeight
等屬性會強制瀏覽器同步計算佈局,應儘量避免頻繁使用這些屬性。 -
脫離文件流 如果該DOM元素有設定動畫,可以透過將
position
改為absolute
或是fixed
,來脫離文件流,避免影響到其他元素的佈局。 -
用visibility代替display
visibility: hidden
仍會保留元素在文檔中的空間,而display: none
會移除元素並觸發Reflow。因此,在適當的情況下,使用visibility
可以避免Reflow。
3.2 優化Repaint的方法
-
使用CSS3硬件加速 使用
transform
、opacity
等屬性可以觸發硬件加速,減少Repaint的開銷。 -
避免頻繁改變樣式 將頻繁變化的樣式提取到單獨的class中,通過切換class來改變樣式。
-
避免使用高代價的CSS屬性 某些CSS屬性,如
box-shadow
、border-radius
等,會導致頻繁的Repaint,應避免在高頻操作中使用這些屬性。
四、實戰案例分析
案例: 大型列表的渲染
在渲染大量列表元素時,頻繁的 Reflow 和 Repaint 會嚴重影響性能。以下是兩種渲染方式的對比。
1. 效能差的渲染方式
下面直接將每個新創建的元素附加到 DOM 中,每次附加元素都會觸發頁面 Reflow 和 Repaint,導致性能下降。
我們打開Chrome的Devtool並使用Performance,點擊錄製後,點擊"添加列表" button。
我們能看到Layout約莫811.05ms
接著試著去記錄滾動,能看到有許多的paint部分(綠色)
2. 使用 Document Fragment 的優化渲染方式
為了提升性能,可以使用 Document Fragment。它是一個輕量的 DOM,只會存在於記憶體中,可以在記憶體中操作子節點,最後再將其附加到即時的 DOM 上。
由於 Fragment 位於記憶體中,而不是主 DOM Tree 的一部分,因此向其附加子級不會導致頁面 Repaint。 (See Document: createDocumentFragment() method - Web APIs | MDN)
我們也繼續去紀錄Performance,點開時layout約莫774.28ms
接著去記錄滾動該頁面
通過使用performance,我們能看到頁面的Reflow和Repaint次數會顯著減少,從而提高渲染性能。
五、結論
理解和掌握Reflow和Repaint的概念,是讓我們理解如何提升頁面性能。通過合理的優化策略,我們可以顯著降低這些過程的開銷,從而提升網頁的響應速度和用戶體驗。