性能優化調研系列文章
《前端性能優化(上)》 主要說明了:
- 為什么要進行前端性能優化?
- 如何衡量前端性能?
這一篇主要記錄了瀏覽器的加載、渲染過程的調研結果和理解。(待詳細梳理 提供動畫版本)
瀏覽器加載、渲染過程
有一道經典的前端面試題: 從輸入一個url到頁面展示發生了什么? 涉及到計算機網絡、操作系統、web等各個方面,從軟件到硬件,從協議到實現,每一個點都可以展開深入的學習,所以很考察面試者的綜合能力和某一個方面的深入掌握程度。
關于這部分知識,可以參考李兵老師的《瀏覽器原理》 課程介紹,下面的內容很大一部分 都是引用他的課程內容;另外《瀏覽器是如何工作的》也非常深入的介紹了瀏覽器工作的原理。
如果你想要有更深入的理解和掌握,建議你跟著大神一起來實現一個簡單的瀏覽器引擎
不聞不若聞之 聞之不若見之 見之不若知之 知之不若行之 學至于行而止矣 -- 荀子
瀏覽器架構
瀏覽器導航
圖片來源: 極客時間
- 瀏覽器主進程處理用戶輸入
- 瀏覽器主進程 通知網絡進程 發起真正的請求
- 網絡進程接收到請求頭信息之后 發送給瀏覽器主進程
- 瀏覽器主進程 接收到網絡進程發送的頭消息之后 發送“提交導航 (CommitNavigation)”消息到渲染進程
- 渲染進程 收到“提交導航”消息之后,直接和網絡進程建立數據管道,接受html數據
- 渲染進程接收html數據完成之后,會通知瀏覽器主進程:確認提交
- 主進程收到渲染進程的“確認提交”消息之后,就開始更新瀏覽器的狀態:loading、前進、后退、url、當前頁面
以上 整個導航流程就走完了。
瀏覽器渲染流水線
一旦渲染進程發送 “確認提交”給瀏覽器主進程,就會開始解析頁面加載子資源。
-
html parsing
圖片來自google 文檔- 解析html -
生成DOM樹
圖片來源 google文檔- 生成DOM樹 樣式計算:
Recaculate Style
將樣式信息(來自內聯樣式、style、link等)轉換成styleSheets
;
Recaculate Style
使用所有通過css parser
解析得到的style rules
,包括瀏覽器給出的默認樣式,計算出每一個DOM元素最終的style
的值,存儲在ComputedStyle
中。
- 布局階段: 生成
LayoutTree
經過樣式計算階段之后,DOM-Tree
中的節點都有了自己的樣式信息,但是還不知道如何排版,放到哪個位置。
布局階段,就是確定繪制區域的位置和大小,相對來說也是比較復雜。- 對于
block flow
布局來說相對簡單,從上往下依次排列就好
block flow -
inline block
inline-block - 其他更復雜的排版:文字、float、flex、table等等
- 對于
總結來說,經過了布局階段之后,就生成了一個
LayoutTree
layout tree
-
paint
google文檔-繪制- 我們已經通過布局階段,得到了一棵
LayoutTree
,現在我們已經知道了每一個Layout object
的布局、顯示信息 - 分層 生成圖層數LayerTree:z-index、3D轉換、 z-index 等會影響布局對象的顯示順序(層級)請參考 《層疊上下文》
分層 - 根據
LayerTree
,產出一個線性的繪制對象列表(列表中的每一個元素存放著繪制的顯示對象和對應的繪制操作) - 對于單獨的一個
Layout Object
,可能會包含多個顯示對象(Display Item
)(如果你使用過canvas
來繪制一些東西或者使用過一些游戲引擎對這個應該比較熟悉)
- 我們已經通過布局階段,得到了一棵
輸出的display item list
,會作為合成線程的輸出
current display item list: [
{
"chunk": "LayoutBlockFlow DIV id='example'
0x1b94c141c0:LayoutBlockFlow DIV id='example':DrawingPaintPhaseSelfBlockBackgroundOnly:0",
"state": "t:0x3e697bca90 c:0x3e697ac8d0 e:0x3e697ac290",
"displayItems": [
{
"index": 0,
"clientDebugName": "InlineTextBox 'hi'",
"id": "0x1605c60410:InlineTextBox 'hi':DrawingPaintPhaseForeground:0",
"visualRect": "401,357 72x20",
"opaque": false,
"record": [
{
"method": "drawTextBlob",
"params": {
"x": 401.5,
"y": 373,
"paint": {
"color": "#FF333333",
"strokeWidth": 0,
"strokeMiter": 4,
"flags": "AntiAlias",
"filterLevel": "Low",
"strokeCap": "Butt",
"strokeJoin": "Miter",
"styleName": "Fill"
}
}
}
]
}
]
}
]
- 光柵化
- 渲染主線程提交 繪制列表給 合成線程;
- 合成線程將圖層劃分為圖塊(256256 或者 512512)
- 合成線程將圖塊提交給 柵格化線程池
- 渲染主進程 通過柵格化線程 發送圖塊生成位圖的指令發送給GPU,
- GPU 生成圖塊的位圖,保存在GPU內存中
- 一旦所有圖塊都被光柵化 合成線程就會生成一個繪制圖塊的命令——“DrawQuad”,然后將該命令提交給瀏覽器進程
- 瀏覽器進程里面有一個叫 viz 的組件,用來接收合成線程發過來的 DrawQuad 命令,然后根據 DrawQuad 命令,將其頁面內容繪制到內存中,最后再將內存顯示在屏幕上
圖片來源:極客時間
幾個關于渲染流水線的問題
待補充示例demo
- CSS 外鏈下載會阻塞 DOM 的構建嗎?
不會, 參考demo
CSS 外鏈下載會阻塞 布局樹的構建嗎?
會 參考demo-
CSS外鏈文件下載會阻塞后面的js的下載嗎?
不會,現代瀏覽器會在收到html文檔之后預解析,請求所有的資源
image.png -
CSS外鏈文件下載會阻塞js的執行嗎?
會
css的下載阻塞了js的執行 js文件的下載和執行會阻塞 DOM樹的構建嗎?
會