理解瀏覽器關鍵渲染路徑
在in面試被CTO問到的問題,謹以此翻譯來表達對自己知識匱乏的鄙視 :-1: 。
當瀏覽器接收到從服務器發送過來的HTML頁面信息,在將其繪畫渲染到屏幕上之前會有許多的步驟要做。瀏覽器繪制頁面需要做的這一系列行為我們稱為 關鍵渲染路徑。
了解 CRP 的知識對于你理解如何提升網站渲染效率非常有用, CRP 總共有六步:
- 構建DOM樹
- 構建CSSOM樹
- 運行 JavaScript
- 創建渲染樹
- 生成布局
- 繪制頁面
1. 構造DOM樹
DOM(Document Object Model)樹是一個表示全解析過的HTML頁面的對象。從根節點元素 <html> 開始,會逐個創建頁面中的每個元素/文本節點。元素包裹的其他元素會被作為子元素節點,并且每個節點會包含其全部的屬性。例如: <a> 標簽會有 href 屬性與其節點關聯。 舉個 :chestnut: :
<html>
<head>
<title>Understanding the Critical Rendering Path</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Understanding the Critical Rendering Path</h1>
</header>
<main>
<h2>Introduction</h2>
<p>Lorem ipsum dolor sit amet</p>
</main>
<footer>
<small>Copyright 2017</small>
</footer>
</body>
</html>
上述HTML會被解析成如下的DOM樹
HTML很好的特點在于不需要全部加載完成頁面所有內容才來顯示網頁,可以解析完成一部分內容呈現一部分。但是,其他資源比如CSS和JavaScript會阻止頁面的渲染。
2. 構建CSSOM樹
CSSOM(CSS Object Model)是一個表示各個DOM相關樣式的對象,它的表示方法與DOM相似,但是各個節點存在相關的樣式值。不論其是顯示、還是隱式聲明這些樣式。 在 style.css 文件中,我們有如下樣式:
body { font-size: 18px; }
header { color: plum; }
h1 { font-size: 28px; }
main { color: firebrick; }
h2 { font-size: 20px; }
footer { display: none; }
接著會創建如下的CSSOM樹:
CSS一直被認為是一種 渲染阻塞資源 。因此如果在首次加載時沒有全部解析資源內容就無法進行渲染樹的構建。與HTML不同,CSS具有層疊繼承的特性,因此不能進行局部加載。定義在文檔后面的樣式屬性會覆蓋或更改寫之前定義的同類屬性。即,如果如果CSS可以進行局部加載的話會導致出現加載錯樣式的情況。因此表明,CSS必須全部解析之后才能進行下一步。
如果CSS文件適用于當前設備的話,僅僅只是會阻塞渲染。 <link rel="stylesheet"> 標簽可以接受 media 屬性來指定特定樣式寬度的特定媒體查詢。 :chestnut: :如果我們有一個樣式表具有 orientation:landscape 的媒體屬性,并且我們查看該頁面使用 portrait 模式,就不會出現資源加載而產生的渲染阻塞情況。
CSS要會導致腳本阻塞,這是由于JavaScript文件必須等待CSSOM構建結束之后才進行加載。
運行JavaScript
JavaScript被認為是 解析阻塞資源 ,這表示當解析HTML文檔自身時候會被JavaScript給阻塞掉。
當解析器解析到 <script> 標簽時,無論該資源是內部還是外鏈的都會停止解析,并且等到資源被下載并運行結束后才繼續進行解析。這也是為什么如果我們有一個引用了JavaScript文件的元素,它必須被放在可視文檔元素之外的原因。
為避免JavaScript解析阻塞,它可以通過設定 async 屬性來要求其異步加載。
<script async src="script.js">
4 創建渲染樹(Render Tree)
渲染樹是DOM樹和CSSOM樹的結合體,它代表最終會渲染在頁面上的元素的結構對象。它表示只會關注可見內容,對于被隱藏或者CSS屬性 display:none 的屬性,不會被包含在結構內。
使用上面例子的DOM和CSSOM,渲染樹被創建如下:
5. 生成布局
布局決定了瀏覽器視窗大小,它提供了上下文依賴的CSS樣式,如百分比或窗口的單位。視窗尺寸通常通過 <head> 標簽中的 <meta> 中的 viewport 設定來決定。如果不存在該標簽,則通常默認為 980px 。
例如:最常用的 meta viewport 值是設定視窗大小與設備寬度對應:
<meta name="viewport" content="width=device-width,initial-scale=1">
舉個 :chestnut: ,如果用戶使用設備訪問網頁,寬度為1000px。然后整體視窗尺寸就會基于這個寬度值了,比如 50% 就是500px, 10vw 就是100px。
6. 繪制頁面
最后,在繪制頁面步驟。頁面上的所有可見內容都會被轉換為像素并呈現在屏幕上。
具體的繪制時間跟DOM數以及應用的樣式有關。有些樣式會話費更多的執行時間,比如復雜的漸變背景圖片所需要的計算時間遠超過簡單固定背景色。
整合所有
想要看到關鍵渲染路徑的執行流程,可以使用Chrome的 DevTools :
<html>
<head>
<title>Understanding the Critical Rendering Path</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Understanding the Critical Rendering Path</h1>
</header>
<main>
<h2>Introduction</h2>
<p>Lorem ipsum dolor sit amet</p>
</main>
<footer>
<small>Copyright 2017</small>
</footer>
<script src="main.js"></script>
</body>
</html>
可以看關于頁面加載時的事件日志,以下是我們獲得的:
- 發送請求:發送 GET 請求index.html
- 解析HTML然后發送請求:開始解析HTML并構建DOM,然后發送 GET 請求style.css和main.js。
- 解析樣式表:根據style.css生成CSSOM
- 執行計算腳本:執行main.js
- 布局:基于HTML的元視窗標簽,生成布局
- 繪制頁面:繪制網頁
來自:https://github.com/lx7575000/Translation/blob/master/(譯)理解瀏覽器的關鍵渲染路徑/(譯)理解瀏覽器的關鍵渲染路徑.md