Google Web Fundamentals 是一個非常優秀的文檔,里面講到了跟web、瀏覽器、前端的方方面面。我總結一下其中的 Ilya Grigorik 寫的 Critical rendering path 瀏覽器渲染機制部分的內容如下:
幾個概念
1、DOM:Document Object Model,瀏覽器將HTML解析成樹形的數據結構,簡稱DOM。
2、CSSOM:CSS Object Model,瀏覽器將CSS代碼解析成樹形的數據結構。
3、DOM 和 CSSOM 都是以 Bytes → characters → tokens → nodes → object model.
這樣的方式生成最終的數據。如下圖所示: DOM樹的構建過程是一個深度遍歷過程:當前節點的所有子節點都構建好后才會去構建當前節點的下一個兄弟節點。
4、Render Tree:DOM 和 CSSOM 合並后生成 Render Tree,如下圖: Render Tree 和DOM一樣,以多叉樹的形式保存了每個節點的css屬性、節點本身屬性、以及節點的孩子節點。
注意:display:none 的節點不會被加入Render Tree,而visibility: hidden 則會,所以,如果某個節點最開始是不顯示的,設為display:none是更優的。具體可以看這里
瀏覽器的渲染過程
1、Create/Update DOM And request css/image/js:瀏覽器請求到HTML代碼后,在生成DOM的最開始階段(應該是 Bytes → characters 后),並行發起css、圖片、js的請求,無論他們是否在HEAD里。
注意:發起js文件的下載request並不需要DOM處理到那個script節點,比如:簡單的正則匹配就能做到這一點,雖然實際上並不一定是通過正則:)。這是很多人在理解渲染機制的時候存在的誤區
2、Create/Update Render CSSOM:CSS文件下載完成,開始構建CSSOM
3、Create/Update Render Tree:所有CSS文件下載完成,CSSOM構建結束后,和 DOM 一起生成 Render Tree。
4、Layout:有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關系。下一步操作稱之為Layout,顧名思義就是計算出每個節點在屏幕中的位置。
5、Painting:Layout后,瀏覽器已經知道了哪些節點要顯示(which nodes are visible)、每個節點的CSS屬性是什么(their computed styles)、每個節點在屏幕中的位置是哪里(geometry)。就進入了最后一步:Painting,按照算出來的規則,通過顯卡,把內容畫到屏幕上。
以上五個步驟前3個步驟之所有使用 “Create/Update” 是因為DOM、CSSOM、Render Tree都可能在第一次Painting后又被更新多次,比如JS修改了DOM或者CSS屬性。
Layout 和 Painting 也會被重復執行,除了DOM、CSSOM更新的原因外,圖片下載完成后也需要調用Layout 和 Painting來更新網頁。
看Timeline,一目了然
我扒了一段有贊PC首頁的代碼到本地,通過Node跑起來。Node作為Server端,對/js/jquery.js
做了延時2s返回的處理,並且把<script src="http://127.0.0.1:8080/js/jquery.js"></script>
放到導航欄的下面,結果是這樣的:
從上面的Timeline我們可以看出:
- 首屏時間和DomContentLoad事件沒有必然的先后關系
- 所有CSS盡早加載是減少首屏時間的最關鍵
- js的下載和執行會阻塞Dom樹的構建(嚴謹地說是中斷了Dom樹的更新),所以script標簽放在首屏范圍內的HTML代碼段里會截斷首屏的內容。
- script標簽放在body底部,做與不做async或者defer處理,都不會影響首屏時間,但影響DomContentLoad和load的時間,進而影響依賴他們的代碼的執行的開始時間。