前端有個很經典的問題是說下重排和重繪的區別,一般我們會說重排性能低,而重繪性能高。但其實我們可以深入探究一下其中但原因。
重排(回流)
定義
當通過JS或者 CSS 修改元素的幾何屬性,例如改變元素的寬度、高度等,那么瀏覽器會觸發重新布局,解析之后的一系列子階段,這個過程就叫重排。無疑,重排需要更新完整的渲染流水線,所以開銷也是最大的。
圖示
觸發條件
- 添加或者刪除可見的DOM元素
- 元素位置改變
- 元素尺寸改變
- 元素內容改變(例: 一個文本被另一個不同尺寸的圖片替代)
- 頁面渲染初始化(無法避免)
- 瀏覽器窗口尺寸改變
優化方案
- 盡量不要在布局信息改變時做查詢(會導致渲染隊列強制刷新)。
- 合並多次DOM操作。比如用class來改變多個樣式。
- 避免使用table。
- 使用fragment元素(createDocumentFragment)
- 讓元素脫離文檔流。即讓當前元素有自己的圖層。
- 多次修改時把dom 離線 ,修改完再顯示。(display:none)
- 使用采用虛擬DOM的庫,如Vue,React
- will-change: transform 啟用硬件加速
重繪
定義
當通過JS或者 CSS 修改元素的繪制屬性,例如改變元素的背景顏色,那么布局階段將不會被執行,因為並沒有引起幾何位置的變換,所以就直接進入了繪制階段(即生成待繪制列表),然后執行之后的一系列子階段,這個過程就叫重繪。相較於重排操作,重繪省去了布局和分層階段,所以執行效率會比重排操作要高一些。
圖示
觸發條件
- background屬性(background,background-color,background-image,background-position,background-repeat,background-size)
- outline屬性(outline,outline-color,outline-style)
- box-shadow屬性
- border屬性(border-style,border-radius)
- visibility
優化方案
- 合並多次操作
合成
定義
更改一個既不要布局也不要繪制的屬性,渲染引擎將跳過布局和繪制,只執行后續的合成操作,我們把這個過程叫做合成。比如我們使用了 CSS 的 transform 來實現動畫效果,這可以避開重排和重繪階段,直接在非主線程上執行合成動畫操作。這樣的效率是最高的,因為是在非主線程上合成,並沒有占用主線程的資源,另外也避開了布局和繪制兩個子階段,所以相對於重繪和重排,合成能大大提升繪制效率。
圖示
觸發條件
- will-change
- transform屬性改變
- 整個圖層的幾何變換,透明度變換,陰影。
優化方案
- 使用will-change提前聲明,使得渲染引擎將該元素單獨實現一幀。(空間換時間)。
⚠️:每當渲染引擎為一個元素准備一個獨立層的時候,它占用的內存也會大大增加,因為從層樹開始,后續每個階段都會多一個層結構,這些都需要額外的內存,所以你需要恰當地使用 will-change。
參考:
極客時間:瀏覽器工作原理與實踐