前端性能優化——關鍵渲染路徑

總結於: Critical Rendering Path

關鍵渲染路徑與性能優化

從收到 HTML、CSS 和 JavaScript 字節到對其進行必需的處理,從而將它們轉變成渲染的像素這一過程當中有一些中間步驟,優化性能其實就是了解這些步驟中發生了什麼, 即關鍵渲染路徑優化關鍵渲染路徑是指優先顯示與當前用戶操做有關的內容。css

image

經過優化關鍵渲染路徑,咱們能夠顯著縮短首次渲染頁面的時間。 此外,瞭解關鍵渲染路徑還能夠爲構建高性能交互式應用打下基礎。html

渲染一個網頁,瀏覽器須要完成的步驟:jquery

  1. 處理 HTML 標記並構建 DOM 樹。
  2. 處理 CSS 標記並構建 CSSOM 樹。
  3. 將 DOM 與 CSSOM 合併成一個渲染樹。
  4. 根據渲染樹來佈局(Layout),以計算每一個節點的幾何信息。
  5. 將各個節點繪製到屏幕上。

咱們的演示網頁看起來可能很簡單,實際上卻須要完成至關多的工做。若是 DOM 或 CSSOM 被修改,您只能再執行一遍以上全部步驟,以肯定哪些像素須要在屏幕上進行從新渲染web

優化關鍵渲染路徑就是指最大限度縮短執行上述第 1 步至第 5 步耗費的總時間。 這樣一來,就能儘快將內容渲染到屏幕上,此外還能縮短首次渲染後屏幕刷新的時間,即爲交互式內容實現更高的刷新率。ajax

關鍵渲染路徑性能分析

發現和解決關鍵渲染路徑性能瓶頸須要充分了解常見的陷阱。 讓咱們踏上實踐之旅,找出常見的性能模式,從而幫助您優化網頁。segmentfault

讓咱們定義一下用來描述關鍵渲染路徑的詞彙:api

  • 關鍵資源: 可能阻止網頁首次渲染的資源。
  • 關鍵路徑長度: 獲取全部關鍵資源所需的往返次數或總時間。
  • 關鍵字節: 實現網頁首次渲染所需的總字節數,它是全部關鍵資源傳送文件大小的總和。咱們包含單個 HTML 頁面的第一個示例包含一項關鍵資源(HTML 文檔);關鍵路徑長度也與 1 次網絡往返相等(假設文件較小),而總關鍵字節數正好是 HTML 文檔自己的傳送大小。

Hello World 體驗

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Critical Path: No Style</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>

咱們將從基本 HTML 標記和單個圖像(無 CSS 或 JavaScript)開始。讓咱們在 Chrome DevTools 中打開 Network 時間線並檢查生成的資源瀑布:瀏覽器

image

當 HTML 內容可用後,瀏覽器會解析字節,將它們轉換成令牌,而後構建 DOM 樹。請注意,爲方便起見,DevTools 會在底部報告 DOMContentLoaded 事件的時間(216 毫秒),該時間一樣與藍色垂直線相符。HTML 下載結束與藍色垂直線 (DOMContentLoaded) 之間的間隔是瀏覽器構建 DOM 樹所花費的時間 — 在本例中僅爲幾毫秒。性能優化

請注意,咱們的「趣照」並未阻止 domContentLoaded 事件。這證實,咱們構建渲染樹甚至繪製網頁時無需等待頁面上的每一個資產:並不是全部資源都對快速提供首次繪製具備關鍵做用。事實上,當咱們談論關鍵渲染路徑時,一般談論的是 HTML 標記、CSS 和 JavaScript。圖像不會阻止頁面的首次渲染,不過,咱們固然也應該盡力確保系統儘快繪製圖像!服務器

即使如此,系統仍是會阻止圖像上的 load 事件(也稱爲 onload):DevTools 會在 335 毫秒時報告 onload 事件。回想一下,onload 事件標記的點是網頁所需的全部資源均已下載並通過處理的點,這是加載微調框能夠在瀏覽器中中止微調的點(由瀑布中的紅色垂直線標記)。

image

T0 與 T1 之間的時間捕獲的是網絡和服務器處理時間。在最理想的狀況下(若是 HTML 文件較小),咱們只需一次網絡往返即可獲取整個文檔。因爲 TCP 傳輸協議工做方式的緣故,較大文件可能須要更屢次的往返。所以,在最理想的狀況下,上述網頁具備單次往返(最少)關鍵渲染路徑。

使用外部 CSS 文件

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>

image

咱們一樣須要一次網絡往返來獲取 HTML 文檔,而後檢索到的標記告訴咱們還須要 CSS 文件;這意味着,瀏覽器須要返回服務器並獲取 CSS,而後才能在屏幕上渲染網頁。所以,這個頁面至少須要兩次往返才能顯示出來。CSS 文件一樣可能須要屢次往返,所以重點在於最少

這裏有:

  • 2 項關鍵資源
  • 2 次或更屢次往返的最短關鍵路徑長度
  • 9 KB 的關鍵字節

咱們同時須要 HTML 和 CSS 來構建渲染樹。因此,HTML 和 CSS 都是關鍵資源:CSS 僅在瀏覽器獲取 HTML 文檔後纔會獲取,所以關鍵路徑長度至少爲兩次往返。兩項資源相加共計 9KB 的關鍵字節。

使用外部的 Javascript 文件及 CSS 文件

如今,讓咱們向組合內額外添加一個 JavaScript 文件。

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script src="app.js"></script>
  </body>
</html>

咱們添加了 app.js,它既是網頁上的外部 JavaScript 資源,又是一種解析器阻止(即關鍵)資源。更糟糕的是,爲了執行 JavaScript 文件,咱們還須要進行阻止並等待 CSSOM;回想一下,JavaScript 能夠查詢 CSSOM,所以在下載 style.css 並構建 CSSOM 以前,瀏覽器將會暫停。

image

即使如此,若是咱們實際查看一下該網頁的「網絡瀑布」,就會注意到 CSS 和 JavaScript 請求差很少是同時發起的;瀏覽器獲取 HTML,發現兩項資源併發起兩個請求。

image

所以,上述網頁具備如下關鍵路徑特性:

  • 3 項關鍵資源
  • 2 次或更屢次往返的最短關鍵路徑長度
  • 11 KB 的關鍵字節

如今,咱們擁有了三項關鍵資源,關鍵字節總計達 11 KB,但咱們的關鍵路徑長度還是兩次往返,由於咱們能夠同時傳送 CSS 和 JavaScript。瞭解關鍵渲染路徑的特性意味着可以肯定哪些是關鍵資源,此外還能瞭解瀏覽器如何安排資源的獲取時間。讓咱們繼續探討示例。

內聯 JavaScript:

image

咱們減小了一個請求,但 onload 和 domContentLoaded 時間實際上沒有變化。爲何呢?怎麼說呢,咱們知道,這與 JavaScript 是內聯的仍是外部的並沒有關係,由於只要瀏覽器遇到 script 標記,就會進行阻止,並等到 CSSOM 構建完畢。此外,在咱們的第一個示例中,瀏覽器是並行下載 CSS 和 JavaScript,而且差很少是同時完成。在此實例中,內聯 JavaScript 代碼並沒有多大意義。可是,咱們能夠經過多種策略加快網頁的渲染速度。

script 標記添加「async」屬性

向 script 標記添加「async」屬性來解除對解析器的阻止:

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script src="app.js" async></script>
  </body>
</html>

異步(外部)JavaScript:
image

解析 HTML 以後不久即會觸發 domContentLoaded 事件;瀏覽器已得知不要阻止 JavaScript,而且因爲沒有其餘阻止解析器的腳本,CSSOM 構建也可同步進行了。

image

異步腳本具備如下幾個優勢:

  • 腳本再也不阻止解析器,也再也不是關鍵渲染路徑的組成部分。
  • 因爲沒有其餘關鍵腳本,CSS 也不須要阻止 domContentLoaded 事件。
  • domContentLoaded 事件觸發得越早,其餘應用邏輯開始執行的時間就越早。

所以,咱們優化過的網頁如今恢復到了具備兩項關鍵資源(HTML 和 CSS),最短關鍵路徑長度爲兩次往返,總關鍵字節數爲 9 KB。

CSS 媒體類型和媒體查詢

若是 CSS 樣式表只需用於打印,那會如何呢?

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet" media="print">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script src="app.js" async></script>
  </body>
</html>

image

image

由於 style.css 資源只用於打印,瀏覽器沒必要阻止它即可渲染網頁。因此,只要 DOM 構建完畢,瀏覽器便具備了渲染網頁所需的足夠信息。所以,該網頁只有一項關鍵資源(HTML 文檔),而且最短關鍵渲染路徑長度爲一次往返。

優化步驟

爲儘快完成首次渲染,咱們須要最大限度減少如下三種可變因素:

  • 關鍵資源的數量。
  • 關鍵路徑長度。
  • 關鍵字節的數量。

關鍵資源是可能阻止網頁首次渲染的資源。這些資源越少,瀏覽器的工做量就越小,對 CPU 以及其餘資源的佔用也就越少。

一樣,關鍵路徑長度受全部關鍵資源與其字節大小之間依賴關係圖的影響:某些資源只能在上一資源處理完畢以後才能開始下載,而且資源越大,下載所需的往返次數就越多。

最後,瀏覽器須要下載的關鍵字節越少,處理內容並讓其出如今屏幕上的速度就越快。要減小字節數,咱們能夠減小資源數(將它們刪除或設爲非關鍵資源),此外還要壓縮和優化各項資源,確保最大限度減少傳送大小。

優化關鍵渲染路徑的常規步驟以下:

  1. 對關鍵路徑進行分析和特性描述:資源數、字節數、長度。
  2. 最大限度減小關鍵資源的數量:刪除它們,延遲它們的下載,將它們標記爲異步等。
  3. 優化關鍵字節數以縮短下載時間(往返次數)。
  4. 優化其他關鍵資源的加載順序:您須要儘早下載全部關鍵資產,以縮短關鍵路徑長度。

評估方法

做爲每一個可靠性能策略的基礎,準確的評估和檢測必不可少。 沒法評估就談不上優化。本文說明了評估 CRP(關鍵渲染路徑) 性能的不一樣方法。

image

由於 style.css 資源只用於打印,瀏覽器沒必要阻止它即可渲染網頁。因此,只要 DOM 構建完畢,瀏覽器便具備了渲染網頁所需的足夠信息。所以,該網頁只有一項關鍵資源(HTML 文檔),而且最短關鍵渲染路徑長度爲一次往返。

使用 Lighthouse 檢測關鍵請求鏈

關鍵請求鏈這個概念源自關鍵渲染路徑 (CRP) 優化策略。 CRP 經過肯定優先加載的資源以及加載順序,容許瀏覽器儘量快地加載頁面。

在 Lighthouse 的 Chrome 擴展程序版本中,您的報告將生成一個相似以下的圖表:

Initial navigation
|---lighthouse/ (developers.google.com)
    |---/css (fonts.googleapis.com) - 1058.34ms, 72.80KB
    |---css/devsite-googler-buttons.css (developers.google.com) - 1147.25ms, 70.77KB
    |---jsi18n/ (developers.google.com) - 1155.12ms, 71.20KB
    |---css/devsite-google-blue.css (developers.google.com) - 2034.57ms, 85.83KB
    |---2.2.0/jquery.min.js (ajax.googleapis.com) - 2699.55ms, 99.92KB
    |---contributors/kaycebasques.jpg (developers.google.com) - 2841.54ms, 84.74KB
    |---MC30SXJEli4/photo.jpg (lh3.googleusercontent.com) - 3200.39ms, 73.59KB

此圖表表示頁面的關鍵請求鏈。從 lighthouse/ 到 /css 的路徑造成一條鏈。 從 lighthouse/ 到 css/devsite-googler-buttons.css 的路徑造成另外一條鏈。 以此類推。審查的最高得分體現了這些鏈條的數量。 例如,上面的圖表的分數爲七分。

該圖表也詳細列出下載每一個資源花了多少時間,以及下載每一個資源所需的字節數。

您能夠根據此圖表利用如下方式提高您的 CRP:

  • 將關鍵資源數降至最低:消除關鍵資源、延遲關鍵資源的下載並將它們標記爲不一樣步等。
  • 優化關鍵字節數以縮短下載時間(往返次數)。
  • 優化其他關鍵資源的加載順序:儘早下載全部關鍵資產,以縮短關鍵路徑長度。

優化以上任一因素均可提高頁面加載速度。

詳情請參閱關鍵請求鏈

使用 Navigation Timing API 設置您的代碼

結合使用 Navigation Timing API 和頁面加載時發出的其餘瀏覽器事件,您能夠捕獲並記錄任何頁面的真實 CRP 性能。

image

上圖中的每個標籤都對應着瀏覽器爲其加載的每一個網頁追蹤的細粒度時間戳。

那麼,這些時間戳有什麼含義呢?

  • domLoading:這是整個過程的起始時間戳,瀏覽器即將開始解析第一批收到的 HTML 文檔字節。
  • domInteractive:表示瀏覽器完成對全部 HTML 的解析而且 DOM 構建完成的時間點。
  • domContentLoaded:表示 DOM 準備就緒而且沒有樣式表阻止 JavaScript 執行的時間點,這意味着如今咱們能夠構建渲染樹了

    • 許多 JavaScript 框架都會等待此事件發生後,纔開始執行它們本身的邏輯。所以,瀏覽器會捕獲 EventStart 和 EventEnd 時間戳,讓咱們可以追蹤執行所花費的時間。
  • domComplete:顧名思義,全部處理完成,而且網頁上的全部資源(圖像等)都已下載完畢,也就是說,加載轉環已中止旋轉
  • loadEvent:做爲每一個網頁加載的最後一步,瀏覽器會觸發 onload 事件,以便觸發額外的應用邏輯。

HTML 規範中規定了每一個事件的具體條件:應在什麼時候觸發、應知足什麼條件等等。對咱們而言,咱們將重點放在與關鍵渲染路徑有關的幾個關鍵里程碑上:

  • domInteractive 表示 DOM 準備就緒的時間點。
  • domContentLoaded 通常表示 DOM 和 CSSOM 均準備就緒的時間點。

若是沒有阻塞解析器的 JavaScript,則 DOMContentLoaded 將在 domInteractive 後當即觸發。

  • domComplete 表示網頁及其全部子資源都準備就緒的時間點。

PageSpeed 規則和建議

消除阻塞渲染的 JavaScript

要以最快速度完成首次渲染,須要最大限度減小網頁上關鍵資源的數量並(儘量)消除這些資源,最大限度減小下載的關鍵字節數,以及優化關鍵路徑長度。
image

延遲解析 JavaScript

爲了最大限度減小瀏覽器渲染網頁的工做量,應延遲任何非必需的腳本(即對構建首次渲染的可見內容可有可無的腳本)。

CSS 標記爲非關鍵資源

CSS 是構建渲染樹的必備元素,首次構建網頁時,JavaScript 經常受阻於 CSS。確保將任何非必需的 CSS 都標記爲非關鍵資源(例如打印和其餘媒體查詢),並應確保儘量減小關鍵 CSS 的數量,以及儘量縮短傳送時間。

將 CSS 置於文檔 head 標籤內

儘早在 HTML 文檔內指定全部 CSS 資源,以便瀏覽器儘早發現 <link> 標記並儘早發出 CSS 請求。

避免使用 CSS import

一個樣式表可使用 CSS import (@import) 指令從另外一樣式表文件導入規則。不過,應避免使用這些指令,由於它們會在關鍵路徑中增長往返次數:只有在收到並解析完帶有 @import 規則的 CSS 樣式表以後,纔會發現導入的 CSS 資源。

image

參考

相關文章
相關標籤/搜索