一個web頁面由多層構成的
比如掘金:
瀏覽器渲染頁面
在編寫頁面中,我們要知道瀏覽器如何處理 HTML
、JavaScript
和 CSS
。
需要了解並注意五個主要區域, 這些我們擁有控制權的部分,也是畫素至螢幕管道中的關鍵點。
每一步簡介
- 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>
複製程式碼
我們可以看到,box1
和 box2
都綠(重繪)了一次,說明 box1
的變化影響了 box2
。那這個屬性變化的代價是比較大的。
假如是我讓 box1
的位置 向右移動 60px
,我們做如下更改:
document.querySelector('.box1').style.transform = 'translateX(60px)';
複製程式碼
現在 box2
的位置不受影響,直觀地看到 box2
是沒被綠(重繪)的。
提升為合成層(Compositing Layers)
我們在上一步做了優化,box2
已經不受影響,但是box1
依然被重繪,那能不能在優化呢。
答案是能的。
left 這個屬性的改變會造成的影響是:
layout -> painted -> composited
複製程式碼
這個流程可以在 csstriggers 看到。
那現在我們要找到一個 css 屬性,既能讓元素位移,又能造成的影響最小。答案是有的:
transform
:影響最小,直接到達最後一步 Composite。
做如下更改:
box1.style.transform = 'translateX(60px)'
複製程式碼
好像事與願違。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;
複製程式碼
再次觀察效果:
大功告成:
- box1 不在重繪了
- box2 不受影響 我們可以檢視最終的分層效果:
和 ps
裡面的圖層差不多,每一個圖層疊加在一起組成我們看到的網頁。
好處
提升為合成層簡單說來有以下幾點好處:
- 合成層的點陣圖,會交由 GPU 合成,比 CPU 處理要快
- 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
- 對於 transform 和 opacity 效果,不會觸發 layout 和 paint
建議:
由於 transition
animation
也有提升層的作用,所以動畫可以優先考慮 css3 動畫。
物極必反
圖層越多越好嗎?
當然不是。提升合成層也得 消耗額外的記憶體和管理資源,
正所謂切勿提前優化.
正如MDN所說: 如果你的頁面在效能方面沒什麼問題,則不要新增 will-change 屬性來榨取一丁點的速度。 will-change 的設計初衷是作為最後的優化手段,用來嘗試解決現有的效能問題.
參考
前端效能優化之 Composite
關鍵轉譯路徑 Critical Rendering Path
will-change
堅持僅合成器的屬性和管理層計數
最後
如果喜歡本篇文章,可以關注的微信公眾號,如果不嫌煩,還可以把它新增到桌面?。