總結於: Critical Rendering Path
從收到 HTML、CSS 和 JavaScript 字節到對其進行必需的處理,從而將它們轉變成渲染的像素這一過程當中有一些中間步驟,優化性能其實就是了解這些步驟中發生了什麼, 即關鍵渲染路徑。優化關鍵渲染路徑是指優先顯示與當前用戶操做有關的內容。css
經過優化關鍵渲染路徑,咱們能夠顯著縮短首次渲染頁面的時間。 此外,瞭解關鍵渲染路徑還能夠爲構建高性能交互式應用打下基礎。html
渲染一個網頁,瀏覽器須要完成的步驟:jquery
咱們的演示網頁看起來可能很簡單,實際上卻須要完成至關多的工做。若是 DOM 或 CSSOM 被修改,您只能再執行一遍以上全部步驟,以肯定哪些像素須要在屏幕上進行從新渲染。web
優化關鍵渲染路徑就是指最大限度縮短執行上述第 1 步至第 5 步耗費的總時間。 這樣一來,就能儘快將內容渲染到屏幕上,此外還能縮短首次渲染後屏幕刷新的時間,即爲交互式內容實現更高的刷新率。ajax
發現和解決關鍵渲染路徑性能瓶頸須要充分了解常見的陷阱。 讓咱們踏上實踐之旅,找出常見的性能模式,從而幫助您優化網頁。segmentfault
讓咱們定義一下用來描述關鍵渲染路徑的詞彙:api
<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 時間線並檢查生成的資源瀑布:瀏覽器
當 HTML 內容可用後,瀏覽器會解析字節,將它們轉換成令牌,而後構建 DOM 樹。請注意,爲方便起見,DevTools 會在底部報告 DOMContentLoaded 事件的時間(216 毫秒),該時間一樣與藍色垂直線相符。HTML 下載結束與藍色垂直線 (DOMContentLoaded) 之間的間隔是瀏覽器構建 DOM 樹所花費的時間 — 在本例中僅爲幾毫秒。性能優化
請注意,咱們的「趣照」並未阻止 domContentLoaded 事件。這證實,咱們構建渲染樹甚至繪製網頁時無需等待頁面上的每一個資產:並不是全部資源都對快速提供首次繪製具備關鍵做用。事實上,當咱們談論關鍵渲染路徑時,一般談論的是 HTML 標記、CSS 和 JavaScript。圖像不會阻止頁面的首次渲染,不過,咱們固然也應該盡力確保系統儘快繪製圖像!服務器
即使如此,系統仍是會阻止圖像上的 load 事件(也稱爲 onload):DevTools 會在 335 毫秒時報告 onload 事件。回想一下,onload 事件標記的點是網頁所需的全部資源均已下載並通過處理的點,這是加載微調框能夠在瀏覽器中中止微調的點(由瀑布中的紅色垂直線標記)。
T0 與 T1 之間的時間捕獲的是網絡和服務器處理時間。在最理想的狀況下(若是 HTML 文件較小),咱們只需一次網絡往返即可獲取整個文檔。因爲 TCP 傳輸協議工做方式的緣故,較大文件可能須要更屢次的往返。所以,在最理想的狀況下,上述網頁具備單次往返(最少)關鍵渲染路徑。
<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>
咱們一樣須要一次網絡往返來獲取 HTML 文檔,而後檢索到的標記告訴咱們還須要 CSS 文件;這意味着,瀏覽器須要返回服務器並獲取 CSS,而後才能在屏幕上渲染網頁。所以,這個頁面至少須要兩次往返才能顯示出來。CSS 文件一樣可能須要屢次往返,所以重點在於最少。
這裏有:
咱們同時須要 HTML 和 CSS 來構建渲染樹。因此,HTML 和 CSS 都是關鍵資源:CSS 僅在瀏覽器獲取 HTML 文檔後纔會獲取,所以關鍵路徑長度至少爲兩次往返。兩項資源相加共計 9KB 的關鍵字節。
如今,讓咱們向組合內額外添加一個 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 以前,瀏覽器將會暫停。
即使如此,若是咱們實際查看一下該網頁的「網絡瀑布」,就會注意到 CSS 和 JavaScript 請求差很少是同時發起的;瀏覽器獲取 HTML,發現兩項資源併發起兩個請求。
所以,上述網頁具備如下關鍵路徑特性:
如今,咱們擁有了三項關鍵資源,關鍵字節總計達 11 KB,但咱們的關鍵路徑長度還是兩次往返,由於咱們能夠同時傳送 CSS 和 JavaScript。瞭解關鍵渲染路徑的特性意味着可以肯定哪些是關鍵資源,此外還能瞭解瀏覽器如何安排資源的獲取時間。讓咱們繼續探討示例。
內聯 JavaScript:
咱們減小了一個請求,但 onload 和 domContentLoaded 時間實際上沒有變化。爲何呢?怎麼說呢,咱們知道,這與 JavaScript 是內聯的仍是外部的並沒有關係,由於只要瀏覽器遇到 script 標記,就會進行阻止,並等到 CSSOM 構建完畢。此外,在咱們的第一個示例中,瀏覽器是並行下載 CSS 和 JavaScript,而且差很少是同時完成。在此實例中,內聯 JavaScript 代碼並沒有多大意義。可是,咱們能夠經過多種策略加快網頁的渲染速度。
向 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:
解析 HTML 以後不久即會觸發 domContentLoaded 事件;瀏覽器已得知不要阻止 JavaScript,而且因爲沒有其餘阻止解析器的腳本,CSSOM 構建也可同步進行了。
異步腳本具備如下幾個優勢:
所以,咱們優化過的網頁如今恢復到了具備兩項關鍵資源(HTML 和 CSS),最短關鍵路徑長度爲兩次往返,總關鍵字節數爲 9 KB。
若是 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>
由於 style.css 資源只用於打印,瀏覽器沒必要阻止它即可渲染網頁。因此,只要 DOM 構建完畢,瀏覽器便具備了渲染網頁所需的足夠信息。所以,該網頁只有一項關鍵資源(HTML 文檔),而且最短關鍵渲染路徑長度爲一次往返。
爲儘快完成首次渲染,咱們須要最大限度減少如下三種可變因素:
關鍵資源是可能阻止網頁首次渲染的資源。這些資源越少,瀏覽器的工做量就越小,對 CPU 以及其餘資源的佔用也就越少。
一樣,關鍵路徑長度受全部關鍵資源與其字節大小之間依賴關係圖的影響:某些資源只能在上一資源處理完畢以後才能開始下載,而且資源越大,下載所需的往返次數就越多。
最後,瀏覽器須要下載的關鍵字節越少,處理內容並讓其出如今屏幕上的速度就越快。要減小字節數,咱們能夠減小資源數(將它們刪除或設爲非關鍵資源),此外還要壓縮和優化各項資源,確保最大限度減少傳送大小。
優化關鍵渲染路徑的常規步驟以下:
做爲每一個可靠性能策略的基礎,準確的評估和檢測必不可少。 沒法評估就談不上優化。本文說明了評估 CRP(關鍵渲染路徑)
性能的不一樣方法。
由於 style.css 資源只用於打印,瀏覽器沒必要阻止它即可渲染網頁。因此,只要 DOM 構建完畢,瀏覽器便具備了渲染網頁所需的足夠信息。所以,該網頁只有一項關鍵資源(HTML 文檔),而且最短關鍵渲染路徑長度爲一次往返。
關鍵請求鏈這個概念源自關鍵渲染路徑 (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 和頁面加載時發出的其餘瀏覽器事件,您能夠捕獲並記錄任何頁面的真實 CRP 性能。
上圖中的每個標籤都對應着瀏覽器爲其加載的每一個網頁追蹤的細粒度時間戳。
那麼,這些時間戳有什麼含義呢?
domContentLoaded:表示 DOM 準備就緒而且沒有樣式表阻止 JavaScript 執行的時間點,這意味着如今咱們能夠構建渲染樹了。
HTML 規範中規定了每一個事件的具體條件:應在什麼時候觸發、應知足什麼條件等等。對咱們而言,咱們將重點放在與關鍵渲染路徑有關的幾個關鍵里程碑上:
若是沒有阻塞解析器的 JavaScript,則 DOMContentLoaded 將在 domInteractive 後當即觸發。
要以最快速度完成首次渲染,須要最大限度減小網頁上關鍵資源的數量並(儘量)消除這些資源,最大限度減小下載的關鍵字節數,以及優化關鍵路徑長度。
爲了最大限度減小瀏覽器渲染網頁的工做量,應延遲任何非必需的腳本(即對構建首次渲染的可見內容可有可無的腳本)。
CSS 是構建渲染樹的必備元素,首次構建網頁時,JavaScript 經常受阻於 CSS。確保將任何非必需的 CSS 都標記爲非關鍵資源(例如打印和其餘媒體查詢),並應確保儘量減小關鍵 CSS 的數量,以及儘量縮短傳送時間。
儘早在 HTML 文檔內指定全部 CSS 資源,以便瀏覽器儘早發現 <link> 標記並儘早發出 CSS 請求。
一個樣式表可使用 CSS import (@import) 指令從另外一樣式表文件導入規則。不過,應避免使用這些指令,由於它們會在關鍵路徑中增長往返次數:只有在收到並解析完帶有 @import 規則的 CSS 樣式表以後,纔會發現導入的 CSS 資源。