繼續上篇《瀏覽器位址列裡輸入URL後的全過程》
前言
為什麼要了解瀏覽器的渲染原理?瞭解瀏覽器的渲染原理有什麼好處?我們做前端開發為什麼非要了解瀏覽器的原理?直接把網頁做出來,什麼需求,直接一把梭,擼完收工不好嗎。
但是經常會有人會問,什麼是重排和重繪?
重排也叫迴流(Reflow
),重繪(Repaint
),會影響到瀏覽器的效能,給使用者的感覺就是網頁訪問慢,或者網頁會卡頓,不流暢,從而使網頁訪問量下降。
所以,想要儘可能的避免重排和重繪,就需要了解瀏覽器的渲染原理。
瀏覽器工作流程
上圖我們可以看出,瀏覽器會解析三個模組:
HTML
,SVG
,XHTML
,解析生成DOM
樹。CSS
解析生成CSS
規則樹。JavaScript
用來操作DOM API
和CSSOM API
,生成DOM Tree
和CSSOM API
。
解析完成後,瀏覽器會通過已經解析好的DOM Tree
和 CSS
規則樹來構造 Rendering
Tree
。
-
Rendering Tree
渲染樹並不等同於DOM
樹,因為一些像Header
或display:none
的東西就沒必要放在渲染樹中了。 -
CSS
的Rule Tree
主要是為了完成匹配並把CSS Rule
附加上Rendering
。 -
Tree
上的每個Element
。也就是DOM
結點,即Frame
。然後,計算每個Frame
(也就是每個Element
)的位置,這又叫layout
和reflow
過程。 -
最後通過呼叫作業系統
Native GUI
的API
繪製。
不同核心的瀏覽器渲染
上圖是webkit
核心的渲染流程,和總體渲染流程差不多,要構建HTML
的DOM Tree
,和CSS
規則樹,然後合併生成Render Tree
,最後渲染。
這個是Mozilla
的Gecko
渲染引擎。總體看來渲染流程差不多,只不過在生成渲染樹或者
Frame
樹時,兩者叫法不一致,webkit
稱之為Layout
,Gecko
叫做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。
- 計算
offsetWidth
和offsetHeight
。
如果當前網頁含有一些動畫,或者固定不動元素的網頁時,由於滾動也會發生重排,一旦發生滾動,當前瀏覽器所承受的壓力很大,就會造成網頁的卡頓,掉幀等情況。
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
放入記憶體中,等操作完畢後,再appendChild
到DOM
元素中。 - 不要經常獲取同一個元素,可以第一次獲取元素後,用變數儲存下來,減少遍歷時間。
- 儘量少使用
dispaly:none
,可以使用visibility:hidden
代替,dispaly:none
會造成重排,visibility:hidden
會造成重繪。 - 不要使用
Table
佈局,因為一個小小的操作,可能就會造成整個表格的重排或重繪。 - 使用
resize
事件時,做防抖和節流處理。 - 對動畫元素使用
absolute / fixed
屬性。 - 批量修改元素時,可以先讓元素脫離文件流,等修改完畢後,再放入文件流。
最後
參考文章: