avatarSchaos

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

2262

Abstract

素大小數值時,瀏覽器便會強制觸發一次 Reflow、以確保程式能取到正確的位置。而這也是許多網站的滑鼠滾輪事件監聽沒寫好,就讓整個網站超卡的原因!</p><h2 id="9fb4">Repaint</h2><p id="0ce0">經過了 Reflow 的計算,Repaint 的任務是要把計算結果轉換成螢幕上的實際像素顯示。相比於 Reflow ,Repaint 就單純多了,任何可見元素的樣式變更,最後都必然需要重新繪製到畫面上,這是難以避免的效能開銷。</p><blockquote id="5dfc"><p><i>更詳細的 CSS 屬性觸發對照表,可以參考 <a href="https://csstriggers.com/">CSS Triggers</a></i></p></blockquote><h1 id="74a2">拯救你的網頁效能</h1><p id="209c">瀏覽器開發者也知道這些渲染步驟很吃效能,所以前輩們在實作時便早已寫好了優化規則:</p><ol><li>如同 Reflow 段落提到的,由於 Reflow 極耗效能,瀏覽器會 <b>自動批次執行</b></li><li>當 DOM 元素的樣式被修改觸發前述步驟時,瀏覽器會依據修改的屬性而 <b>自動省略</b> 不需要的步驟,重新渲染頁面。</li></ol><blockquote id="1030"><p>修改 <code>width</code> → Reflow → Repaint</p></blockquote><blockquote id="257d"><p>修改 <code>color</code> → Repaint</p></blockquote><p id="8d95">理解上述規則之後,讀者您應該就能猜到該如何優化 CSS 效能了吧?,以下也提供幾種常見的方法:</p><h2 id="b57e">屬性替換</h2><p id="4ab7">將物理屬性的變化,換成相似的其他屬性變化,來節省 Reflow 的效能開銷:</p><ul><li><code>translate</code> 取代 <code>top</code> 等定位屬性</li><li>由於表格的物理屬性會互相影響,容易改一格就整張表 Reflow,可以的話請不要用 table 排版</li></ul><figure id="9cd0"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/0*gsk7X9sRSivfQ9yq.jpg"><figcaption></figcaption></figure><h2 id="1124">批次修改</h2><p id="e13b">當需要用 JavaScript 修改樣式時,盡量讓樣式能批次生效:</p><ul><li>替換 class name 或修改 <code>cssText</code>,而不是逐個設定 style 屬性</li><li>透過 <code>el.cloneNode()</code> 複製一份 DOM,在上面修改樣式後,在替換原本的 DOM</li><li>透過 <code>document.createDocumentFragment()</code> 建立 Document Fragments,編輯 DOM 後再加回主 DOM Tree 中</li></ul><h2 id="68c9">減少影響範圍</h2><p id="d410">如果 Reflow 是避免不了的,那就只能減少影響範圍了:</p><ul><li>盡量避免 DOM、CSSOM Tree 的層級過深,加快 Reflow 的計算</li><li>程式取得元素物理屬性時,將結果暫存起來,不要重複觸發計算</li><li>改動頻繁的地方 <b>建立單獨的圖層</b><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context">stacking context</a></li></ul><h1 id="3c17">結語</h1><p id="1fea">以上就是這次的回流與重繪的說明,之後在撰寫 CSS 時,記得要考慮到對畫面渲染效能的影響喔!如果對於內文有疑問或不清楚的地方,都歡迎您在底下留言回應一起討論;明天將接續今天的 CSS 主題,敬請期待!</p><h2 id="c13e">參考資料</h2><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment">MDN — Document.createDocumentFragment()</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context">MDN — The stacking context</a></li><li>

Options

<a href="https://csstriggers.com/">CSS Triggers</a></li><li><a href="https://gist.github.com/faressoft/36cdd64faae21ed22948b458e6bf04d5">faressoft: DOM Performance</a></li><li><a href="https://juejin.im/post/5a9372895188257a6b06132e">掘金 — reflow和repaint引发的性能问题</a></li></ul><blockquote id="392e"><p>本文同步發表於 2020 IT 邦幫忙鐵人賽</p></blockquote><div id="2392" class="link-block"> <a href="https://ithelp.ithome.com.tw/articles/10217427"> <div> <div> <h2>03. [CSS] Reflow 及 Repaint 是什麼? - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天</h2> <div><h3>昨天 聊了 HTML 控制 JavaScript 載入順序的方法,設定得宜可以提升不少效能;那麼今天就來聊聊可能造成 CSS 影響效能的其中兩位兇手: 回流( Reflow)& 重繪( Repaint )。…</h3></div> <div><p>ithelp.ithome.com.tw</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*0MaSM0TCrugnDF5a)"></div> </div> </div> </a> </div> <figure id="7c4b"> <div> <div> <img class="ratio" src="http://placehold.it/16x9"> <iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fbutton.like.co%2Fin%2Fembed%2Fgary_chu%2Fbutton%2F&amp;display_name=LikeCoin&amp;url=https%3A%2F%2Fbutton.like.co%2Fgary_chu&amp;image=https%3A%2F%2Fstorage.googleapis.com%2Flikecoin-foundation.appspot.com%2Flikecoin_store_user_gary_chu_main%3FGoogleAccessId%3Dfirebase-adminsdk-eyzut%2540likecoin-foundation.iam.gserviceaccount.com%26Expires%3D2430432000%26Signature%3DaXjCoEbugypUZYmd75UwfXbcjRPYubsy0yc6%252B%252F7hKFeIA0qXeK2XqNEq%252Fys92tnzbgKR4uDsZAuItD40lcXj8Y8FMaL0c2XLbU%252F4Tb%252BUhxON5sa%252Bbr0COKv1WF7tzXKi0u%252BaQru2HBaGDnSXLdQzTRwvGA584CPni2xMbd0OryOsfI1%252FWBFvGwM5EJLQ%252BXgDmm9j1Cr7oFhk9RUx%252F5mLOCvSPn7Nt01bN%252BLilHnq0pmGpvDssDwo53WgdYitBCv%252BlstkgGwIkrKJzbl4LFkIyG%252Flv2fA1g8ixnaJuwMVvLwoq5p%252Br9x05h847wChnTYR1nSntmBGFHtcfqd%252Fofc%252F0A%253D%253D&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=like" allowfullscreen="" frameborder="0" height="212" width="485"> </div> </div> </figure></iframe></div></div></figure></article></body>

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

昨天 聊了 HTML 控制 JavaScript 載入順序的方法,設定得宜可以提升不少效能;那麼今天就來聊聊可能造成 CSS 影響效能的其中兩位兇手:回流Reflow)& 重繪Repaint)。

但在進入正題之前,我們要先認識一下瀏覽器的渲染機制。

本系列文已經重新編校彙整編輯成冊,並正式出版囉!

前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法好評販售中!

喜歡我文章內容的讀者們,歡迎您前往購買支持

瀏覽器的渲染步驟

image by faressoft

上圖是瀏覽器解析網頁的示意圖,為方便理解,我們就簡單拆解成幾個步驟:

  1. 從 HTML 檔解析出 DOM Tree
  2. 從 CSS 檔解析出 CSSOM Tree
  3. 兩者疊加後產生 Render Tree
  4. Reflow:計算出 Render Tree 上各個元素的物理屬性,如位置、大小、及是否看得見(visible)
  5. Repaint:將計算結果轉為實際的像素,畫到畫面上

Reflow

如同前述,這個步驟會由 Render Tree 的根結點出發,逐步計算出每一個元素的位置、大小,以及是否被其他元素遮擋等屬性,需要耗費大量的運算資源;也因為是要計算出這些屬性,只要是有可能牽扯到這些屬性的操作,都會觸發 Reflow,例如:

1. 設定 CSS 屬性

  • 大小:widthheight
  • 浮動: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

拯救你的網頁效能

瀏覽器開發者也知道這些渲染步驟很吃效能,所以前輩們在實作時便早已寫好了優化規則:

  1. 如同 Reflow 段落提到的,由於 Reflow 極耗效能,瀏覽器會 自動批次執行
  2. 當 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 主題,敬請期待!

參考資料

本文同步發表於 2020 IT 邦幫忙鐵人賽

前端三十
CSS
Front End Development
Reflow
Repaint
Recommended from ReadMedium