will-change — 那些前端開發應該要知道的小事(七)

alexianalexian
2023-08-27
那些前端開發應該要知道的小事will-change

你要做動畫你要先說啊

前端開發少不免會需要使用 CSS 實作過渡動畫,不管是使用 transform 搭配 transition 進行簡單的漸變動畫、或是使用 animation 和 keyframes 以應用更為複雜的動畫,動畫的順暢度都對使用者體驗有非常大的影響,因些我們需要處理好動畫的效能;如果處理不慎,輕則導致動畫無法正常呈現,重則影響畫面無法操作,所以處理好動畫效能也是非常重要的技能。不知道你有沒有遇過無論如何實作動畫,畫面就是會出現卡頓的情況發生呢?

在前端開發中,有一個專門處理這種情境的的屬性:will-change。

什麼是 will-change?要怎麼使用?

will-change 是一個 CSS 屬性,它可以告訴瀏覽器哪些元素將要發生改變,進而優化這些元素的渲染方式。讓瀏覽器可以為該元素的渲染提前進行相關的準備工作,以避免在元素發生改變時出現卡頓或者閃爍等的問題。

以下是 will-change 的寫法:

.element {
  will-change: property; /* transform, opacity, etc. */
}

其中 property 是指將要發生改變的屬性,可以是任何CSS屬性,比如 transform、opacity 等等。

以下是一個使用的例子,頁面有 10000 個會進行動畫的元素,由於在開始各元素並沒有進行過渡效果,當 JS 更新了元素的屬性,瀏覽器根據前面提及的 Composite 步驟,讓每個元素建立渲染層,但因為數量眾多,影響了動畫的順暢度;

這個時候為元素加入 will-change: transform; ,讓瀏覽器得到眾多元素即將有動畫產生,進而準備好元素建立渲染層,讓動畫得以更順暢的呈現;

Codepen 範例在此(建議使用 safari 更能看出差異)

will-change 實際做了什麼?

雖然 will-change 的使用很直覺,但 will-change 在瀏覽器底層實際做了什麼呢?在瞭解它做了什麼之前。我們先來看看瀏覽器是怎麼渲染畫面的。

瀏覽器是怎麼渲染畫面的?

我們知道每個 HTML 標籤都會產出 Document Object Model (俗稱 DOM),而瀏覽器會根據 HTML 產生 DOM tree;除此以外,還會根據載入的 CSS ,產出 CSS Object Model (俗稱 CSSOM)tree;最後瀏覽器會配對 DOM 與 CSSOM,產出 rendering tree,也就是我們最終可以在瀏覽器視窗看到的內容。

而 rendering tree 產出後,會透過三個主要步驟,渲染出內容:

  1. Reflow:為 DOM 進行佈局/形狀的步驟,例如根據 CSS 的 width, height 屬性製作尺寸、或是根據 top, left 屬性定位
  2. Repaint:為 DOM 進行外觀或色彩變化的步驟,例如根據 color, background 繪製顏色內容。
  3. Composite:瀏覽器會根據 CSS 最佳化成不同的渲染層進行 Reflow 與 Repaint 步驟,生成的渲染層將會送至 GPU 進行合成,最終成為我們在瀏覽器看到的畫面

Chrome devTools 的 layer panel 可以檢查目前畫面上的渲染層

Chrome devTools 的 layer panel 可以檢查目前畫面上的渲染層

這邊我們著重看的部分是 Composite,瀏覽器生成渲染層考量的因素有很多,比如元素的 CSS 包含 opacity, transform 或是 z-index,或是是否在進行過渡效果等。透過建立渲染層進行畫面的合成,後續渲染畫面時可以減少 Reflow 和 Repaint 的次數,以達到提升效能的效果。

瞭解過這些基礎原理後,我們再來看看 will-change 可以做些什麼。

will-change 的真面目

我們用一個比較簡單的例子,搭配 Chrome devtools 中的 layer panel 來看;一般來說,瀏覽器會根據畫面中的元素的狀態而建立渲染層,會建立渲染層的原因包含 z-index、position 等,其中包含元素使用 transform 進行動畫(codepen 範例):

未加入 will-change 的渲染層

可以看到在未加上 will-change 屬性的狀態,在動畫進行時,會為 .block 建立單獨的渲染層,在結束後,該渲染層便會移除。這樣的好處是可以有效的運用系統資源,避免浪費;

而為 .block 加上 will-change 後的狀況(codepen 範例) :

加入 will-change 的渲染層

可以看到在一般情況下有動畫才出現的渲染層,變成長駐的出現;這是瀏覽器在接受到 will-change 屬性的內容,預先得知 .block 會有 transform 動態的變化,所以讓渲染層維持,以利動畫可以隨時的呈現,代價則是需要更多系統資源去維持。

不知道大家以往有沒有使用過 transform: translate3d(0,0,0) 這個 hack 來優化動畫,其原理和 will-change 是相同的喔!

使用 will-change 的注意事項

從我前述兩個範例可以看出,will-change 其實不應該任意使用,在使用前需要考量使用的情境,以下是使用 will-change 的注意事項:

1. 請把 will-change 當作最終手段

一般情況下瀏覽器會為你的網頁應用做資源的最佳化處理,而且使用時需要系統資源作為代價;will-change 應該在真正遭遇到效能瓶頸的時候再做補充。如果以藥物作比喻,will-change 更像是腎上腺素或止痛藥;我們不會在還沒遇到危險之前就先做緊急處理。

2. 使用 javascript 動態加入/移除

由於 will-change 的行為會在瀏覽器接受到屬性變化後進行最佳化,情境許可的狀況下,可以考慮知道動畫需要出現前一段時間,再為元素加入該屬性。並在動畫效果完成後,為其進行移除(比如使用 transitionend 事件監聽)

3. 產生的渲染層可能會影響現有排版

由於 will-change 屬性可能會為元素建立單獨的渲染層,層疊的順序就有可能影響到原有畫面的排版,因此必須注意使用的地方

總結

會使用 will-change 的情境其實寥寥可數,通常是你發現畫面的動畫太卡頓,或是因為某些複雜的動畫而產生 bug 時,才會考慮使用 will-change 進行優化;該屬性也並非什麼新鮮事,除了 IE 家族外其他瀏覽器也已經悉數支援,因此可以放心的使用。

回過頭來說,假如遇到動畫卡頓的問題,你更應該先想想原有的程式碼邏輯是否有優化的可能,直到真的想不到更優解,will-change 才是你最後的殺手鐧。

參考來源

  • 一篇文章說清瀏覽器解析和CSS(GPU)動畫優化
  • https://developer.mozilla.org/en-US/docs/Web/CSS/will-change

上一篇

Modern Web Conference 2023 觀後感

下一篇

解決 Gatsby.js 中 markdown 無法使用相對路徑引入 gif/pdf 問題


Alex Ian
Alex Ian

ReactJS, VueJS 畫畫/寫作/前端開發

Alex Ian
Alex Ian

ReactJS, VueJS 畫畫/寫作/前端開發


共29篇文章
文章分類
  • 前端開發14
  • JavaScript7
  • 那些前端開發應該要知道的小事5
  • Gatsby.js3
  • ChromeExtension2
  • NetlifyCMS2
  • 生產力工具2
  • 關於我1
  • 非Coding1
  • Svelte1
  • raycast1
  • toolRecommend1
  • productivity1
  • MutationObserver1
  • observer pattern1
  • 開發效率1
  • will-change1
  • GitHubCopilot1
  • 生產力1
  • ChatGPT1
  • AI1
  • Copilot1
  • WebAssembly1
  • SEO1
  • GoogleSearchConsole1
  • react1
  • applescript1
  • 閱讀1
  • css1
  • grid1
  • micro-frontend1

Build with GatsbyJS and React 18.2.0. Hosted on Netlify.

The original code is open source and available at calpa/gatsby-starter-calpa-blog

Copyright ©AlexIan's blog 2025.

  1. 從頭開始上架 Chrome extension

    上回提到我們在本地端建好一個開發版本的 Chrome …
  2. Gatsby 部落格更新記錄 1.0

    首先把 Blog 連結貼在最前面:https://alex-ian.me/ 許多工程師 …
  3. Debounce & Throttle - 那些前端開發應該要知道的小事(一)

    前言 也許一開始接觸前端開發的新手們,都有使 …
  4. 使用 GitHub Copilot 一個月心得

    後悔自己為何沒有早點課金 在 Modern Web Conference 2023 觀後感 …
  5. 什麼是 Headless CMS?以探索 Netlify CMS 為例

    小弟的部落格使用 Gatsby.js 框架來架設,作為一個靜 …
  6. Modern Web Conference 2023 觀後感

    https://modernweb.tw/ 前言 Modern Web Conference (MWC) 作為每年度經典的網頁開 …
  7. 什麼是語法糖(Syntactic sugar)? - 2024 你要知道的 JS 語法糖 🍭

    語法糖能吃嗎? 在算數學時,不知道你什麼時候開 …
  8. 閉包 - 那些前端開發應該要知道的小事(三)

    前言 開始這個系列的原因,是因為雖然在程式中 …
  9. Raycast — Mac 提升工作效率的啟動工具

    Mac 的使用者,應該對 Spotlight 這個系統預設的啟動工具 …
  10. 使用 Notion 管理我的年度目標 暨 2023 回顧 & 2024 展望

    都進入 2024 才來展望 想要成為流量的奴隸,自然要 …
  11. 慢半拍來初嚐 Svelte

    Svelte 都出來兩年多的框架現在才來玩,有夠慢半拍 …
  12. 從頭開始學習開發 Chrome extension (v3 版本)

    Chrome extension 經歷過 v1, v2 後,來到了 v3,雖然曾小量接觸 v2 …
  13. 留言簿

    歡迎留言
  14. addEventListener的第三個參數 - 那些前端開發應該要知道的小事(二)

    前言 開始這個系列的原因,是因為雖然在程式中 …
  15. 在中大型企業下微前端的實現與挑戰

    我們先聊工程面,人的部分先不要 小弟任職前端 …
  16. will-change — 那些前端開發應該要知道的小事(七)

    你要做動畫你要先說啊 前端開發少不免會需要 …
  17. 關於我

    關於我 我是Alex Ian,是個小小的前端工程師,出來工 …
  18. 開發效率提升 - 操作鍵盤 & 快捷鍵篇

    我國中的電腦課,會教導學生倉頡和速成輸入法 …
  19. macbook-pro(retina) late2012 換電池記錄

    這篇文章其實與Coding無關,主要是記錄mbp換電池的 …
  20. polyfill是什麼?

    人在江湖,身不由己,在接案的過程中,總有一些你 …
所有結果