首先,我們需要先明白一個問題
用戶請求的資源到達渲染引擎后如何作業?
- 構建DOM樹
- 構建CSSOM樹和樣式計算
- 構建渲染樹
下面是網上的一張圖片,可以很清晰的說明前三步

- 布局階段
布局階段會從渲染樹的根節點開始遍歷,然后確定每個節點物件在頁面上的確切的大小和位置,所有相對的測量值也都會被轉換為螢屏內的絕對像素值,
- 繪制
瀏覽器主行程將默認的圖層(普通檔案流的元素)和多個獨立圖層交給GPU行程,GPU行程再將各個圖層合成,最終顯示在螢屏上,
哪些元素會獨立圖層?<video><iframe><canvas>等,
下面是一張網站的圖層圖片,可見,所有元素并不是都在一個圖層上展示,
理解了網頁渲染頁面的程序,也明白了圖層,下面來看一下什么是回流?什么是重繪?
什么是重繪?
當渲染樹中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風格,而不會影響布局的,比如background-color,則就叫稱為重繪,
哪些元素會導致重繪?
Color
Border-style
Border-radius
Visibility
Text-decoration
Background
Background-img
Outline-color
Box-shadow
……
什么是回流?
當渲染樹中的一部分(或全部)因為元素的規模尺寸,布局,隱藏等改變而需要重新構建,這就稱為回流,每個頁面至少需要一次回流,就是在頁面第一次加載的時候,這時候是一定會發生回流的,因為要構建渲染樹,
引發問題:回流的代價要遠高于重繪,回流會造成重繪,重繪不一定回流,回流瀏覽器需要重新渲染頁面,從而影響性能,
哪些因素會導致回流?
- 添加或洗掉可見的元素
- 元素位置改變
- 元素尺寸改變:邊距,填充,邊框,寬度和高度
- 頁面渲染初始化
- 瀏覽器視窗尺寸改變:resize 事件發生時
- 獲取元素的偏移量屬性,例如:
scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft、offsetWidth、offsetHeight,瀏覽器為了保證值的正確性,會回流取最新的值
等等…
哪些元素會導致回流?
- 與盒子模型有關的屬性
width
Height
Padding
Margin
Display
Border-width
Border
- 與位置和浮動有關的
width
Height
Padding
Margin
Display
Border-width
Border
- 與文字和行間距等等有關的
width
Height
Padding
Margin
Display
Border-width
Border
我們既然明白了什么是重繪和回流,以及哪些因素和元素導致了回流和重繪,并且明白了重繪回流在頁面渲染程序中造成哪些影響,那我們如何優化呢?
瀏覽器渲染層面如何優化?
主要從下面兩個方面來避免發生重繪回流
- 將頻繁回流的DOM元素作為一個獨立的圖層,那么這個DOM元素的回流只影響這一個圖層,其他圖層不會受影響,
- 避免使用觸發回流的css屬性,
優化舉例
- 對于影片新建圖層,對于頻繁變化的元素應該為其加一個 transform 屬性, transform:
translateZ(0px);使用這個屬性會開啟GPU加速,瀏覽器會認為這種情況重繪比較頻繁,默認會單獨變成一個圖層, - 對于視頻使用video 標簽,
- gif圖會頻繁觸發重繪,但是它在標簽中,所以沒有獨立圖層,我們可以將其獨立出來圖層,這樣可以使重繪的范圍變小,
- 不要使用table布局,可能很小的一個小改動會造成整個table的重新布局
- 不要逐條的修改DOM的樣式,如果想修改一個DOM節點的多個css屬性值,可以預先定義好class,一次性修改
- 不要把DOM節點的屬性值放在一個回圈里當成回圈里的變數
- 使用translate替代top
下面拿幾個具體的例子來展示:
1. 多次改變DOM元素的屬性,并不會像之前說的那樣,引發多次回流重繪,而是將這些合并為一次,因為瀏覽器中有緩沖機制,通過這個緩沖機制,能夠減少回流重繪的次數,比如控制在100ms周期內,多次回流重繪合并為一次,這屬于瀏覽器自身的性能優化,


由上面結果可以看出,多次改變DOM的屬性,并不會發生多次回流重繪,事實上,只導致了一次,但是我們還是要做自己代碼的優化,盡量減少回流重繪的次數,我們不能讓瀏覽器自身的機制來保證代碼的性能,每個瀏覽器自身的優化性能也都不同,所以我們盡量使我們寫出來的代碼變得強壯,
2. 回圈中獲取offsetWidth

結果如下:回流發生了100次,也就是獲取多少次offsetTop,就發生了多少次回流,并且根據下面的圖片可以看出,已經報紅色了,因為這其中進行了100次的強制性回流,事實上,獲取offsetTop這個屬性的時候,已經不再遵循上面講的瀏覽器自身性能優化的規律了,獲取一次,會留一次,


為什么會導致這個問題呢?
因為我們在讀取元素offsetTop之前,先在該元素中添加了一個新的元素,瀏覽器為了給我們最新的offsetTop值,需要重新布局來獲取,
如果在獲取值之前,不對該元素進行寫的操作,會怎樣呢?


事實上,讀寫分離后,值發生了一次回流,
拋開讀寫分離這件事,那上面的例子我們應該如何優化呢,可以將獲取的offsetTop的值取出來之后作為快取,不要每次回圈都獲取,


對于這個例子,也有人說,是因為每次回圈中都需要讀寫DOM元素導致的性能降低,JS每次都讀寫DOM元素無疑會大大降低性能,但不會發生回流,
3. 合理利用GPU硬體加速
在做各種前端影片實作時,充分利用GPU加速,可以使影片更加流暢,用戶體驗更好,利用GPU這種硬體層次的加速,我們永遠不用擔心會觸及它的上限,因為在觸及上限前,一定先會觸發瀏覽器的上限,
一些css3的屬性會觸發GPU加速:
- Translate:有translateX,translateY,translate3D等,主要用于處理影像的平移,
- Rotate:同樣也有x,y,z軸的轉換,主要用來處理影像的轉換,
- Scale:主要用于處理影像的縮放,
- opacity:用于改變影像的透明度,
- Filter:濾鏡,
上述的幾大效果都會觸發瀏覽器的GPU加速機制,在做影片時,盡量通過css3的影片代替傳統的定時器輪詢,用改變top/left/width/height值得方式來完成影片的渲染,這樣既可以做出優美的影片,也不會對渲染性能造成影響,
4.將頻繁引發頁面回流的元素獨立圖層
重繪和回流對頁面渲染期間的性能影響很大,影片的元素會頻繁的觸發頁面的回流,為了減少影片元素對其他元素的影響,從而減少頁面重繪回流的次數,我們需要將影片元素提升為獨立層,
影片會頻繁觸發回流和重繪


獨立圖層后不再發生回流


既然將元素獨立圖層后不會發生回流重繪,或者說重繪的范圍大大縮小,那么是不是只要將元素獨立圖層就一定能提高性能呢?
對于類似影片元素這種頻繁引發頁面回流重繪的元素來說,獨立出來一個圖層是能更好的提高渲染方面的性能,但是創建一個圖層并不是免費的,它需要消耗額外的記憶體和管理資源,在記憶體資源有限的設備上,合成層帶來的性能改善,可能遠遠趕不上過多合成層開銷給頁面性能帶來的負面影響,比如一些較舊的手機和電腦,開啟加速,無論是電池還是發熱量都會比平時高,
同時,由于每個渲染層的紋理都需要上傳到GPU處理,因此我們還需要考慮CPU和GPU之間的帶寬問題,以及有多大記憶體供GPU處理這些紋理,
元素未獨立圖層


元素獨立圖層


以上文章為個人理解加上實踐結果,如有理解不正確的地方,還請指正,感激不盡,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/250237.html
標籤:其他
上一篇:JavaScript的預編譯
下一篇:webpack筆記總結(二)
