瀏覽器渲染原理(效能優化之如何減少重排和重繪)

光光同學發表於2019-01-11

瀏覽器渲染原理(效能優化之如何減少重排和重繪)

繼續上篇《瀏覽器位址列裡輸入URL後的全過程

前言

為什麼要了解瀏覽器的渲染原理?瞭解瀏覽器的渲染原理有什麼好處?我們做前端開發為什麼非要了解瀏覽器的原理?直接把網頁做出來,什麼需求,直接一把梭,擼完收工不好嗎。

但是經常會有人會問,什麼是重排重繪

重排也叫迴流Reflow),重繪Repaint),會影響到瀏覽器的效能,給使用者的感覺就是網頁訪問慢,或者網頁會卡頓,不流暢,從而使網頁訪問量下降。

所以,想要儘可能的避免重排重繪,就需要了解瀏覽器的渲染原理

瀏覽器工作流程

瀏覽器渲染原理(效能優化之如何減少重排和重繪)

上圖我們可以看出,瀏覽器會解析三個模組:

  • HTML,SVG,XHTML,解析生成DOM樹。
  • CSS解析生成CSS規則樹。
  • JavaScript用來操作DOM APICSSOM API,生成DOM TreeCSSOM API

解析完成後,瀏覽器會通過已經解析好的DOM TreeCSS規則樹來構造 Rendering Tree

  • Rendering Tree 渲染樹並不等同於DOM樹,因為一些像Headerdisplay:none的東西就沒必要放在渲染樹中了。

  • CSSRule Tree主要是為了完成匹配並把CSS Rule附加上Rendering

  • Tree上的每個Element。也就是DOM結點,即Frame。然後,計算每個Frame(也就是每個Element)的位置,這又叫layoutreflow過程。

  • 最後通過呼叫作業系統Native GUIAPI繪製。

不同核心的瀏覽器渲染

瀏覽器渲染原理(效能優化之如何減少重排和重繪)
上圖是webkit核心的渲染流程,和總體渲染流程差不多,要構建HTMLDOM Tree,和CSS規則樹,然後合併生成Render Tree,最後渲染。

瀏覽器渲染原理(效能優化之如何減少重排和重繪)
這個是MozillaGecko渲染引擎。
總體看來渲染流程差不多,只不過在生成渲染樹或者Frame樹時,兩者叫法不一致,webkit稱之為LayoutGecko叫做Reflow

渲染順序

瀏覽器渲染原理(效能優化之如何減少重排和重繪)

  • 當瀏覽器拿到一個網頁後,首先瀏覽器會先解析HTML,如果遇到了外鏈的css,會一下載css,一邊解析HTML
  • css下載完成後,會繼續解析css,生成css Rules tree,不會影響到HTML的解析。
  • 當遇到<script>標籤時,一旦發現有對javascript的引用,就會立即下載指令碼,同時阻斷文件的解析,等指令碼執行完成後,再開始文件的解析。

瀏覽器渲染原理(效能優化之如何減少重排和重繪)

  • DOM樹和CSS規則樹已經生成完畢後,構造 Rendering Tree
  • 呼叫系統渲染頁面。

什麼情況會造成重排和重繪。

重排意味著元件的幾何尺寸變了,我們需要重新驗證並計算Render Tree。是Render Tree的一部分或全部發生了變化。這就是Reflow,或是Layout

重排因為要重新計算Render Tree,而且每一個DOM Tree都有一個reflow方法,一旦某個節點發生重排,就有可能導致子元素和父元素甚至是同級其他元素的reflow,浪費大量的時間重新驗證Render Tree

因此,重排的成本要比重繪高很多。

以下操作會導致重排重繪

  • 刪除,增加,或者修改DOM元素節點。
  • 移動DOM的位置,開啟動畫的時候。
  • 修改CSS樣式,改變元素的大小,位置時,或者將使用display:none時,會造成重排;修改CSS顏色或者visibility:hidden等等,會造成重繪
  • 修改網頁的預設字型時。
  • Resize視窗的時候(移動端沒有這個問題),或是滾動的時候。
  • 內容的改變,(使用者在輸入框中寫入內容也會)。
  • 啟用偽類,如:hover。
  • 計算offsetWidthoffsetHeight

如果當前網頁含有一些動畫,或者固定不動元素的網頁時,由於滾動也會發生重排,一旦發生滾動,當前瀏覽器所承受的壓力很大,就會造成網頁的卡頓,掉幀等情況。


var bstyle = document.body.style; // cache
 
bstyle.padding = "20px"; // reflow, repaint
bstyle.border = "10px solid red"; //  再一次的 reflow 和 repaint
 
bstyle.color = "blue"; // repaint
bstyle.backgroundColor = "#fad"; // repaint
 
bstyle.fontSize = "2em"; // reflow, repaint
 
// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('dude!'));

複製程式碼

以上邏輯,幾乎每一步都會造成重排重繪,如果瀏覽器像這樣處理的話,可能現代的瀏覽器沒有我們使用的那麼流暢了。
因此瀏覽器有一個機制,會把需要重排重繪的先積累著,然後一次性進行重排重繪

當然,不是所有的情況瀏覽器都是這樣處理的,比如resize或者修改預設字型,對於這些操作,瀏覽器會立馬進行重排

所以我們在監聽resize事件時,一般我們都會做防抖節流

如何減少重排和重繪

  • 儘量避免style的使用,對於需要操作DOM元素節點,重新命名className,更改className名稱。
  • 如果增加元素或者clone元素,可以先把元素通過documentFragment放入記憶體中,等操作完畢後,再appendChildDOM元素中。
  • 不要經常獲取同一個元素,可以第一次獲取元素後,用變數儲存下來,減少遍歷時間。
  • 儘量少使用dispaly:none,可以使用visibility:hidden代替,dispaly:none會造成重排visibility:hidden會造成重繪
  • 不要使用Table佈局,因為一個小小的操作,可能就會造成整個表格的重排重繪
  • 使用resize事件時,做防抖節流處理。
  • 對動畫元素使用absolute / fixed屬性。
  • 批量修改元素時,可以先讓元素脫離文件流,等修改完畢後,再放入文件流。

最後

參考文章:

How browsers work

瀏覽器的渲染原理簡介

前端效能優化之重排和重繪

相關文章