瀏覽器層合成與頁面效能優化

酷吧你我他發表於2019-12-11

一個web頁面由多層構成的

比如掘金:

瀏覽器層合成與頁面效能優化

瀏覽器渲染頁面

在編寫頁面中,我們要知道瀏覽器如何處理 HTMLJavaScriptCSS。 需要了解並注意五個主要區域, 這些我們擁有控制權的部分,也是畫素至螢幕管道中的關鍵點。

image

每一步簡介

  • JavaScript。 一般來說,我們會使用 JavaScript 來實現一些視覺變化的效果。比如用 jQuery 的 animate 函式做一個動畫、對一個資料集進行排序或者往頁面裡新增一些 DOM 元素等。當然,除了 JavaScript,還有其他一些常用方法也可以實現視覺變化效果,比如:CSS Animations、Transitions 和 Web Animation API。
  • 樣式計算。 此過程是根據匹配選擇器(例如 .headline 或 .nav > .nav__item)計算出哪些元素應用哪些 CSS 規則的過程。從中知道規則之後,將應用規則並計算每個元素的最終樣式。
  • 佈局。 在知道對一個元素應用哪些規則之後,瀏覽器即可開始計算它要佔據的空間大小及其在螢幕的位置。網頁的佈局模式意味著一個元素可能影響其他元素,例如 元素的寬度一般會影響其子元素的寬度以及樹中各處的節點,因此對於瀏覽器來說,佈局過程是經常發生的。
  • 繪製。 繪製是填充畫素的過程。它涉及繪出文字、顏色、影像、邊框和陰影,基本上包括元素的每個可視部分。繪製一般是在多個表面(通常稱為層)上完成的。
  • 合成。 由於頁面的各部分可能被繪製到多層,由此它們需要按正確順序繪製到螢幕上,以便正確渲染頁面。對於與另一元素重疊的元素來說,這點特別重要,因為一個錯誤可能使一個元素錯誤地出現在另一個元素的上層。

ps: 當然,不是每一步更改都會遵循上圖這個流程。

每一步不是必經的

比如:

  • 更改了元素的佈局相關的屬性:width, height, 位置... 那麼瀏覽器就會檢查其他元素,自動 重排一次
  • 更改了元素的 color, 陰影... 不會影響頁面的佈局,那麼瀏覽器就會跳過佈局。這就是我們平常說的:重排一定引起重繪,重繪不一定引起重排
  • 如果更改了 一個既不會佈局,也不會繪製的屬性,那麼瀏覽器直接跳到最後一步,不得不說,這是最高效的

使用 csstriggers 可以詳細看到 css 屬性改變時觸發的流程。

如何提升繪製的效能

儘量使用影響較少的屬性

舉個?:

<div class="box box1">1</div>
<div class="box box2"> 2 </div>
  <script>
  const box1 = document.querySelector('.box1');
  setTimeout(() => {
     box1.style.display = 'none'
  }, 3000);
  </script>
複製程式碼

relayout

我們可以看到,box1box2 都綠(重繪)了一次,說明 box1 的變化影響了 box2。那這個屬性變化的代價是比較大的。

假如是我讓 box1 的位置 向右移動 60px,我們做如下更改:

document.querySelector('.box1').style.transform = 'translateX(60px)';
複製程式碼

leftrepaint

現在 box2的位置不受影響,直觀地看到 box2是沒被綠(重繪)的。

提升為合成層(Compositing Layers)

我們在上一步做了優化,box2 已經不受影響,但是box1 依然被重繪,那能不能在優化呢。 答案是能的。 left 這個屬性的改變會造成的影響是:

layout -> painted -> composited
複製程式碼

這個流程可以在 csstriggers 看到。

image
那現在我們要找到一個 css 屬性,既能讓元素位移,又能造成的影響最小。
答案是有的: transform:影響最小,直接到達最後一步 Composite。 做如下更改:

box1.style.transform = 'translateX(60px)'
複製程式碼

transform-normal

好像事與願違。box1, box2 都被重繪了。
這裡因為: 他們都在一個層上,一個元素的變化也影響了其他元素。瀏覽器會聯合需要繪製的區域,而導致整個螢幕重繪。
為了直接到達最後一步 Composite。其實這裡有個條件: 更改屬性所在的元素應處於其自身的合成層,如果沒在,我們可以提升為合成層` 這樣就不會影響其他元素,而能減少繪製區域。

提升為合成層的原因有一下幾種

這裡我大概羅列了這麼多

  • video
  • 有 3D transform
  • backface-visibility 為 hidden
  • 對 opacity、transform、fliter、backdropfilter 應用了 animation 或者 transition(需要是 active 的 animation 或者 transition,當 animation 或者 transition 效果未開始或結束後,提升合成層也會失效)
  • will-change 設定為 opacity、transform、top、left、bottom、right(其中 top、left 等需要設定明確的定位屬性,如 relative 等)
  • 重疊原因

box1 上面做如下更改:

will-change: transform;
複製程式碼

再次觀察效果:

transform-layer

大功告成:

  • box1 不在重繪了
  • box2 不受影響 我們可以檢視最終的分層效果:

image

ps 裡面的圖層差不多,每一個圖層疊加在一起組成我們看到的網頁。

好處

提升為合成層簡單說來有以下幾點好處:

  • 合成層的點陣圖,會交由 GPU 合成,比 CPU 處理要快
  • 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
  • 對於 transform 和 opacity 效果,不會觸發 layout 和 paint

建議:
由於 transition animation 也有提升層的作用,所以動畫可以優先考慮 css3 動畫。

物極必反

圖層越多越好嗎? 當然不是。提升合成層也得 消耗額外的記憶體和管理資源
正所謂切勿提前優化.
正如MDN所說: 如果你的頁面在效能方面沒什麼問題,則不要新增 will-change 屬性來榨取一丁點的速度。 will-change 的設計初衷是作為最後的優化手段,用來嘗試解決現有的效能問題.

參考

前端效能優化之 Composite
關鍵轉譯路徑 Critical Rendering Path
will-change
堅持僅合成器的屬性和管理層計數

最後

如果喜歡本篇文章,可以關注的微信公眾號,如果不嫌煩,還可以把它新增到桌面?。

瀏覽器層合成與頁面效能優化

相關文章