1. 程式人生 > >理解關鍵的渲染路徑

理解關鍵的渲染路徑

當瀏覽器從伺服器接收到一個HTML頁面的請求時,到螢幕上渲染出來要經過很多個步驟。瀏覽器完成這一系列的執行,或者說渲染出來我們常常稱之為“關鍵渲染路徑”(Critical Rendering Path)。

理解CRP(Critical Rendering Path)相關的知識可以更好的提高網站的效能。那麼理解我們從下面六個部分來理解CRP相關的知識:

  • 列表內容
  • 列表內容
  • 構建DOM樹
  • 樹建CSSOM樹
  • 執行JavaScript
  • 建立Render樹
  • 生成佈局
  • 繪製(Painting)

構建DOM樹

DOM(文件物件模型)樹是一個完全解析的HTML頁面物件。從<html>

根元素開始到頁面中每個元素和文字的節點。元素巢狀在其他元素內則表示為子節點,每個節點包含完整的屬性元素。例如一個<a>元素,它就有與之相關的href節點。

來看下面這個簡單的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>

這將會建立一個像下面這樣的DOM樹:

HTML比較好的是它可以執行部分。完整的文件不需要載入的內容會在頁面的開始呈現。然而,比如CSS和JavaScript可以阻止頁面的呈現。

構建CSSOM樹

CSSOM(CSS物件模型)是一個表示DOM樣式的物件。它類似於DOM,但是是每個節點相關的樣式,包括他們是否顯式宣告或隱式繼承。

比如,在style.css檔案中對上面的DOM有這樣的一些樣式:

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必須充分解析才能繼續下一個階段。

如果只運用於當前裝置,CSS檔案只被認為阻塞。<link rel="stylesheet">標籤可以接受一個media屬性,可以用來指定樣式適用於何種媒體。例如,我們有一個樣式表,它的media屬性設定為orientation:landscape時,如果在portrait模式下檢視頁面,那麼這個樣式表不會被認為是一個阻塞資源。

CSS還會阻塞JavaScript。那是因為JavaScript檔案必須要等CSSOM構建完才會執行。

執行JavaScript

JavaScript被認為是一個“解析器阻塞資源”。這意味著解析的HTML文件本身就會被JavaScript阻塞。

當解析器讀到<script>標記,不管是內部的還是外部的,它停止獲取(如果是外部的)並執行它。這個為什麼,如果我們有一個JavaScript檔案,該檔案引用文件中的元素,那麼它必須放在這個元素的後面。

為了避免JavaScript解析器造成阻塞,可以新增async屬性,非同步載入它。

<script async src="script.js">

建立Render樹

Render樹是DOM和CSSOM的組合。這個樹代表最終在頁面上呈現的東西。這意味著它只抓住了可見的內容,將不包括設定了hidden的元素和CSS設定了display:none的元素。

使用上面的DOM和CSSOM,構建的Render樹如下圖所示:

生成佈局

佈局根據CSS樣式設定的大小來決定頁面在視窗中的尺寸。視窗的大小是由<head>viewport標記來決定,如果沒有提供這個標記,預設使用的視窗寬度是980px

例如,常見的設定視窗的meta標籤,指定的大小對應裝置寬度:

<meta name="viewport" content="width=device-width,initial-scale=1">  

如果使用者訪問頁面寬度是基於裝置的寬度1000px。那一半的視窗就是500px10vw就是100px

繪製(Painting)

最後就是繪製(Painting)步驟,把頁面可見的內容轉化為畫素在螢幕上顯示。

繪製需要多少時間這取決於DOM的大小以及應用的樣式。有一些樣式需要更多的執行時間。例如,一個複雜的漸變背景影象比一個單一顏色背景繪製需要更多的時間。

把它們結合起來

我們可以在DevTools中看到關鍵渲染路徑的過程。在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>  

如果我們看頁面的載入日誌,將看到如下這樣的資料:

  • 傳送請求:傳送GET,請求index.html
  • 解析HTML併發送請求:解析HTML並且構建DOM樹,傳送GET請求,請求style.cssmain.js
  • 解析樣式:根據style.css建立CSSOM
  • 指令碼評估:main.js評估
  • 佈局:基於HTML中的視窗meta生成佈局
  • 繪製:繪製頁面

基於這些資訊,我們可以決定如何優化關鍵渲染路徑。我也將在後續的文章中深入的介紹這方面的知識。