當瀏覽器從服務(wù)器接收到一個HTML頁面的請求時,到屏幕上渲染出來要經(jīng)過很多個步驟得哆。瀏覽器完成這一系列的運行,或者說渲染出來我們常常稱之為 “關(guān)鍵渲染路徑”(Critical Rendering Path)翠桦。
理解CRP(Critical Rendering Path)相關(guān)的知識可以更好的提高網(wǎng)站的性能葵擎。那么理解我們從下面六個部分來理解CRP相關(guān)的知識:
- 構(gòu)建DOM樹
- 樹建CSSOM樹
- 運行JavaScript
- 創(chuàng)建Render樹
- 生成布局
- 繪制(Painting)
構(gòu)建DOM樹
DOM(文檔對象模型)樹是一個完全解析的HTML頁面對象。從<html>根元素開始到頁面中每個元素和文本的節(jié)點麸粮。元素嵌套在其他元素內(nèi)則表示為子節(jié)點溉苛,每個節(jié)點包含完整的屬性元素。例如一個元素弄诲,它就有與之相關(guān)的href節(jié)點愚战。
來看下面這個簡單的DOM示例:
<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>
這將會創(chuàng)建一個像下面這樣的DOM樹:
HTML比較好的是它可以執(zhí)行部分。完整的文檔不需要加載的內(nèi)容會在頁面的開始呈現(xiàn)齐遵。然而寂玲,比如CSS和JavaScript可以阻止頁面的呈現(xiàn)。
構(gòu)建CSSOM樹
CSSOM(CSS對象模型)是一個表示DOM樣式的對象梗摇。它類似于DOM拓哟,但是是每個節(jié)點相關(guān)的樣式,包括他們是否顯式聲明或隱式繼承伶授。
比如断序,在style.css文件中對上面的DOM有這樣的一些樣式:
body { font-size: 18px; }
header { color: plum; }
h1 { font-size: 28px; }
main { color: firebrick; }
h2 { font-size: 20px; }
footer { display: none; }
這將會構(gòu)建像下面這樣的一個CSSOM樹:
CSS被認為是 “渲染阻塞資源”流纹。這意味著渲染樹(見下文)的構(gòu)建離不開延續(xù)一個資源的解析完成度。不像HTML违诗,CSS不能只用部分捧颅,因為CSS是具有繼承層疊特性。文檔后面定義的樣式可以覆蓋前面定義的樣式较雕。所以,如果我們開始使用CSS時挚币,之前的樣式表會被認為已經(jīng)全部解析完亮蒋。這也意味著CSS必須充分解析才能繼續(xù)下一個階段。
如果只運用于當前設(shè)備妆毕,CSS文件只被認為阻塞慎玖。==<link rel="stylesheet">== 標簽可以接受一個media屬性,可以用來指定樣式適用于何種媒體笛粘。例如趁怔,我們有一個樣式表,它的media屬性設(shè)置為orientation:landscape時薪前,如果在portrait模式下查看頁面润努,那么這個樣式表不會被認為是一個阻塞資源。
CSS還會阻塞JavaScript示括。那是因為JavaScript文件必須要等CSSOM構(gòu)建完才會執(zhí)行铺浇。
運行JavaScript
JavaScript被認為是一個 “解析器阻塞資源”。這意味著解析的HTML文檔本身就會被JavaScript阻塞垛膝。
當解析器讀到<script>標記鳍侣,不管是內(nèi)部的還是外部的,它停止獲群鹩怠(如果是外部的)并運行它倚聚。這個為什么,如果我們有一個JavaScript文件凿可,該文件引用文檔中的元素惑折,那么它必須放在這個元素的后面。
為了避免JavaScript解析器造成阻塞矿酵,可以添加==async==屬性唬复,異步加載它。
<script async src="script.js">
創(chuàng)建Render樹
Render樹是DOM和CSSOM的組合全肮。這個樹代表最終在頁面上呈現(xiàn)的東西敞咧。這意味著它只抓住了可見的內(nèi)容,將不包括設(shè)置了hidden的元素和CSS設(shè)置了display:none的元素辜腺。
使用上面的DOM和CSSOM休建,構(gòu)建的Render樹如下圖所示:
生成布局
布局根據(jù)CSS樣式設(shè)置的大小來決定頁面在窗口中的尺寸乍恐。視窗的大小是由<head>中viewport標記來決定,如果沒有提供這個標記测砂,默認使用的視窗寬度是980px茵烈。
例如,常見的設(shè)置視窗的meta標簽砌些,指定的大小對應(yīng)設(shè)備寬度:
<meta name="viewport" content="width=device-width,initial-scale=1">
如果用戶訪問頁面寬度是基于設(shè)備的寬度1000px呜投。那一半的視窗就是500px,10vw就是100px存璃。
繪制(Painting)
最后就是繪制(Painting)步驟仑荐,把頁面可見的內(nèi)容轉(zhuǎn)化為像素在屏幕上顯示。
繪制需要多少時間這取決于DOM的大小以及應(yīng)用的樣式纵东。有一些樣式需要更多的執(zhí)行時間粘招。例如,一個復(fù)雜的漸變背景圖像比一個單一顏色背景繪制需要更多的時間偎球。
把它們結(jié)合起來
我們可以在DevTools中看到關(guān)鍵渲染路徑的過程洒扎。在Chrome瀏覽器中,點擊Timeline選項衰絮。
拿文章前面的示例(這里添加了<script>標簽):
<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>
如果我們看頁面的加載日志袍冷,將看到如下這樣的數(shù)據(jù):
- 發(fā)送請求:發(fā)送GET,請求index.html
- 解析HTML并發(fā)送請求:解析HTML并且構(gòu)建DOM樹猫牡,發(fā)送GET請求难裆,請求style.css和main.js
- 解析樣式:根據(jù)style.css創(chuàng)建CSSOM
- 腳本評估:main.js評估
- 布局:基于HTML中的視窗meta生成布局
- 繪制:繪制頁面
基于這些信息,我們可以決定如何優(yōu)化關(guān)鍵渲染路徑镊掖。