總體流程如上圖所示,大概可描述爲:javascript
用戶在地址欄輸入關鍵字後,地址欄會根據關鍵字來判斷是搜索內容
仍是請求 URL
。css
https://www.google.com/search?q=google&xxxxxx
www.baidu.com => https://www.baidu.com/
輸入關鍵字後回車,標籤上的圖標變成 loading 狀態,此時頁面沒有當即替換爲目標頁面;須要等待提交文檔階段結束,頁面內容纔會被替換。(見下圖) html
頁面請求資源過程,瀏覽器進程經過 IPC(進程間通訊) 把 URL 請求發送至網絡進程,網絡進程收到後發起真正的 URL 請求。具體過程以下:java
Content-Type
字段判斷響應體的數據類型;如果application/octet-stream
字節流類型,瀏覽器會按下載類型來處理,該請求會被提交給瀏覽器的下載管理器,流程結束。如果 HTML,則進入下個流程:準備渲染進程不一樣主域名的頁面使用不一樣的渲染進程,主域名相同的使用同一個渲染進程;web
瀏覽器進程
發出提交文檔
的消息。按照渲染的時間順序,流水線可分爲: 構建 DOM 樹 -> 樣式計算 -> 佈局 -> 分層 -> 繪製 -> 分塊 -> 光柵化 -> 合成等階段。chrome
每一個階段的流程以下:接收輸入的內容 -> 處理 -> 輸出內容瀏覽器
接收 html 文件 -> html 解析器解析 -> 輸出樹狀解構的 DOM緩存
大致流程:接受 css 文本 -> 計算出 dom 節點的具體樣式 -> 輸出計算完成後的 DOM 樣式安全
具體步驟以下:服務器
接收 css 文本,將其轉爲瀏覽器可理解的結構:stylesheets css 三種來源:內聯、外部引入、style
轉換 stylesheets 中屬性值,使其標準化
body { font-size: 2em }
p {color:blue;}
div { font-weight: bold}
// 標準化後
body { font-size: 32px }
p {color: rgb(0,0,255);}
div { font-weight: 700}
複製代碼
全部子節點都繼承父節點的樣式。計算完後輸出每一個 DOM 節點的樣式(保存在 ComputedStyle 解構內,可在 elements -> computed 標籤下查看)
通過上面過程,有了 DOM 樹和樹中元素的樣式,但每一個元素的位置還不肯定,不足以顯示頁面。
Chrome 在佈局階段的任務:建立佈局樹與佈局計算。
建立佈局樹 遍歷 DOM 中的可見節點,將其添加到佈局樹中。不可見的節點會被忽略,好比:head 標籤下的內容,display 爲 none
佈局計算
計算佈局樹節點的座標位置,計算完執行佈局時會把運算的結果會從新寫入佈局樹中,因此佈局樹既是輸入內容也是輸出內容;此處設計不合理,chrome 正在重構,下一代佈局系統 LayoutNG 試圖分離輸入與輸出。
佈局完以後不會當即繪製,渲染引擎會爲特定的節點生成專用的圖層,並生成一棵對應的圖層樹(LayerTree)
。在開發者工具下的 Layers 標籤能夠看見頁面的分層狀況。
佈局樹和圖層樹的關係
由上圖可得,不是每一個節點都有圖層,若沒有,就屬於它父節點的圖層。什麼狀況下,渲染引擎會爲特定節點建立新圖層?
畫圖例子: 給你一張紙,畫一個背景藍色,中間爲紅色的圓,再在圓上畫一個綠色三角形,如何畫?
圖層繪製與之相似,將一個圖層拆分紅不少小的繪圖指令
,把指令按順序組成列表去繪製。(每一個元素背景、前景、邊框都須要單獨的繪圖指令) 開發者工具 Layers 標籤,選中 document 層,可重現繪製過程,以下。
繪製列表只用來記錄繪製順序與指令,真正的繪製操做由渲染引擎中的合成線程來完成。如上圖,繪製列表準備好後,主線程會把它提交給合成線程。
有的圖層很大,頁面要使用滾動條滾很久才能到底部;經過視口,用戶只能看到一小部分,這種狀況下若是繪製全部圖層內容,會產生很大開銷且不必。
合成線程如何處理?
合成線程會將圖層劃分紅圖塊(tile),大小一般爲(256256,512512)。
柵格化(將圖塊轉爲位圖)
執行。圖塊是柵格化執行的最小單位。渲染進程維護了一個柵格化的線程池,全部圖塊柵格化都在線程池內執行。
柵格化過程會使用 GPU 來加速生成,生成的位圖被保留在 GPU 內存中。(GPU 操做在 GPU 進程中,柵格化在渲染進程中,這個過程涉及跨進程操做。)
當全部圖塊都被柵格化,合成進程會生成一個繪製圖塊的命令DrawQuad
,而後將該命令提交給瀏覽器進程。
瀏覽器進程裏有個 viz 組件,接收到 DrawQuad 命令後,根據其內容,將頁面繪製到內存,最後再將內存顯示到屏幕上。
同一站點共用一個渲染進程,那假設有2個標籤頁是同一站點,我在A標籤頁面寫個死循環,致使頁面卡死,B頁面是否也是卡死了呢?
多個頁面公用一個渲染進程,也就意味着多個頁面公用同一個主線程,全部頁面的任務都是在同一個主線程上執行,這些任務包括渲染流程,JavaScript執行,用戶交互的事件的響應等等,可是若是一個標籤頁裏面執行一個死循環,那麼意味着該JavaScript代碼會一直霸佔主線程,這樣就致使了其它的頁面沒法使用該主線程,從而讓全部頁面都失去響應!
若下載 CSS 文件阻塞了,會阻塞 DOM 樹的合成嗎?會阻塞頁面的顯示嗎?
不會阻塞 DOM 樹合成,html 轉爲 DOM 樹的過程當中,發現文件請求會交給網絡進程去請求文件,渲染進程繼續解析 html。 會阻塞頁面顯示,計算樣式時須要 css 文件資源,若資源阻塞,會等待至網絡超時,network 報出響應錯誤,渲染進程繼續層疊樣式計算。
JS 和 CSS 均可能阻塞 DOM 解析
當從服務器接收HTML頁面的第一批數據時,DOM解析器就開始工做了,在解析過程當中,若是遇到了JS腳本,以下所示:
<html>
<body>
掘金
<script> document.write("--foo") </script>
</body>
</html>
複製代碼
那麼DOM解析器會先執行JavaScript腳本,執行完成以後,再繼續往下解析。
那麼第二種狀況複雜點了,咱們內聯的腳本替換成js外部文件,以下所示:
<html>
<body>
掘金
<script type="text/javascript" src="foo.js"></script>
</body>
</html>
複製代碼
這種狀況下,當解析到JavaScript的時候,會先暫停DOM解析,並下載foo.js文件,下載完成以後執行該段JS文件,而後再繼續往下解析DOM。這就是JavaScript文件爲何會阻塞DOM渲染。
咱們再看第三種狀況,仍是看下面代碼:
<html>
<head>
<style type="text/css" src = "theme.css" /> </head> <body> <p>掘金</p> <script> let e = document.getElementsByTagName('p')[0] e.style.color = 'blue' </script> </body> </html> 複製代碼
當我在JavaScript中訪問了某個元素的樣式,那麼這時候就須要等待這個樣式被下載完成才能繼續往下執行,因此在這種狀況下,CSS也會阻塞DOM的解析。
渲染進程的幀是什麼?
能夠拿放電影電影來解釋,一般,電影的幀速是24,也就是說每秒切換24幅畫面,其中的每幅畫面就是一幀。
理解什麼是幀後,咱們在回過頭看看咱們的頁面。因爲目前大多數設備的屏幕刷新率爲 60 次/秒。所以,若是頁面中有一個動畫、或一個漸變效果、或者用戶正在滾動頁面,那麼瀏覽器渲染動畫的頻率至少要和刷新頻率保持一致,也就是每秒須要更新60次,這樣咱們就能計算出來生成每幀的預算只有(1/60)毫秒,也就是16毫秒多一點(1 秒/ 60 = 16.66 毫秒)。若是超過16毫秒,幀率將降低,而且會出現畫面抖動現象,此現象一般被稱爲卡頓,會對用戶體驗產生負面影響。
因此,若是想要保證畫面的流暢,就須要儘可能下降每幀的渲染時間,因此局部更新流水線顯得很是重要了,能大大減小處理每幀所消耗的時間。