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

關鍵路徑渲染優化


什麼是優化關鍵路徑

  • 優化關鍵路徑的就是優先顯示與用戶操作相關的內容,也就是常說的儘量減少白屏時間或是減少首屏渲染時間。
    在這裏插入圖片描述
  • 好的頁面交互,即使是在服務器處理或是資源還未完全返回期間,也應該儘量渲染部分信息給用戶,而不是讓用戶明顯感知過長白屏時間,以爲頁面卡死。例如Google時,在服務器處理搜索時及時渲染局部信息,而後逐步顯示完整信息。
    在這裏插入圖片描述
  • 接下來,主要研究下從獲取頁面到渲染瀏覽器私底下都做了哪些東西。如下圖,HTML解析成DOM樹,JS可能生成或編輯DOM和編輯CSSOM,然後組成渲染樹渲染在屏幕上。
    在這裏插入圖片描述
  • 瀏覽器渲染解析主要操作,解析HTML生成DOM,解析CSS生成CSSOM,有JS可能改變DOM、CSSOM,兩種結合成渲染樹layout,知道了元素的大小、位置、顏色、層次等信息瀏覽器就可以繪製圖層組合圖層渲染頁面啦。
    在這裏插入圖片描述
  • 卡通圖片更加直觀。
    在這裏插入圖片描述

關鍵指標

宗旨:你不需要去優化你無法測量的東西

  1. 關鍵資源數量
  2. 關鍵資源字節大小
  3. 關鍵路徑長度(RTT:網絡往返次數)
  • 關鍵渲染路徑考慮的時間維度一般是指發起請求到 DOMContentLoaded 這段時間。
    在這裏插入圖片描述
  • 性能監控首屏渲染信息獲取上報通常使用 performance.getEntriesByType('navigation')[0] 獲得。
{
    "name": "https://mp.csdn.net/mdeditor",
    "entryType": "navigation",
    "startTime": 0,
    "duration": 1929.0599999949336,
    "initiatorType": "navigation",
    "nextHopProtocol": "h2",
    "workerStart": 0,
    "redirectStart": 30.985000004875474,
    "redirectEnd": 258.7400000047637,
    "fetchStart": 258.7400000047637,
    "domainLookupStart": 258.7400000047637,
    "domainLookupEnd": 258.7400000047637,
    "connectStart": 258.7400000047637,
    "connectEnd": 258.7400000047637,
    "secureConnectionStart": 0,
    "requestStart": 262.220000004163,
    "responseStart": 316.87500000407454,
    "responseEnd": 319.18999999470543,
    "transferSize": 2113,
    "encodedBodySize": 1834,
    "decodedBodySize": 4430,
    "serverTiming": [],
    "unloadEventStart": 0,
    "unloadEventEnd": 0,
    "domInteractive": 1289.1050000034738,
    "domContentLoadedEventStart": 1289.9250000045868,
    "domContentLoadedEventEnd": 1296.4300000021467,
    "domComplete": 1928.7900000053924,
    "loadEventStart": 1928.859999999986,
    "loadEventEnd": 1929.0599999949336,
    "type": "navigate",
    "redirectCount": 1
}
  • 一般以 domComplete 完成時間爲止,也就說這段時間影響渲染主要的資源和操作纔是重點需要關注的。
    在這裏插入圖片描述
  • 好了有了大致的時間維度的觀念後,然後接着分析瀏覽器做了什麼。

構建對象

瀏覽器渲染頁面需要DOM、CSSOM,所有應該儘快把HTML、CSS給到瀏覽器。

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>
  • 這裏稍微提一下,<meta name="viewport" content="width=device-width,initial-scale=1"> 告訴瀏覽器視口寬度等於設備寬度,初始縮放1:1,去掉這段瀏覽器會默認窗口980px,導致頁面縮放成很小,需要手動放大,很不友好。但頁面沒做適配或響應式佈局設計的話,還不如不加這段,至少用戶通過縮放可以看到想看的部位。
    在這裏插入圖片描述
body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }
  • 下面看出CSS規則會向下層疊,繼承或覆蓋規則。
    在這裏插入圖片描述

  • 和DOM構建不同,DOM可以逐步構建並結合完整的CSSOM渲染。DOM不能和部分CSSOM組成渲染樹 ,你不能爲了提前渲染而渲染出錯誤的樣式信息給用戶,寧願不做也不能做錯。這也就是爲什麼常聽到優化點 CSS儘量放在頭部儘快加載解析在這裏插入圖片描述

  • 字節 -> 字符 -> Token 令牌/標籤/選擇器 -> 對象模型

  • HTML標籤轉化成DOM,CSS選擇器轉化成CSSOM

  • DOM、CSSOM是兩個完全獨立的數據結構

  • CSS會阻塞渲染

  • render tree 保留需要渲染元素的位置、形狀大小、顏色、層級等信息。
    在這裏插入圖片描述

打開Chrome 開發工具 performance 分析中可以看到(PS:即使HTML中沒有style 或 link 樣式標籤,瀏覽器依然有默認樣式。)。

  • Parse HTML 表示DOM構建過程
  • Recalculate Style 表示 CSSOM 構建過程
  • Layout 獲取元素的位置、尺寸信息,更新渲染樹信息。
  • Update Layer Tree 更新元素層級信息
  • Paint、Composite 轉化成屏幕上的像素

優化關鍵渲染路徑就是優化以上過程的總時間。

在這裏插入圖片描述

  • 一個典型的關鍵路徑渲染順序。PS:瀏覽器可能爲了加速性能,在CSS未解析前,提前執行app.js內容,但執行到CSSOM的操作時會暫停JS引擎執行等待CSS加載構建完畢。
    在這裏插入圖片描述

分析關鍵路徑渲染性能

1、無JS、CSS。鏈接(PS:以下圖片只爲舉例說明原理,實際數據不一定對的上,包括瀏覽器更新,實際呈現未必一樣。)

<!DOCTYPE html>
<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>

在這裏插入圖片描述
查看chrome 開發工具 -> network 。HTML文件很小,獲取文件一次網絡往返即可,服務器處理和網絡傳輸大約200ms,DOM完全解析沒有被阻塞僅僅花費了幾毫秒。圖片等某些資源並不會阻塞渲染、DOMContentLoaded 事件,但會影響 onLoad 事件 。(T0到T1的時間是網絡往返和服務器處理時間)

  • DOMContentLoadedonLoad 區別,DOM解析完成之後會調用前者,這時對於JS來說,整個DOM元素都是可交互狀態。後者是所有資源包括圖片等下載完畢後會調用 onLoad
    在這裏插入圖片描述
  • 關鍵路徑資源:1個(html)
  • 關鍵路徑資源大小:5KB(html)
  • 關鍵路徑長度:1次(獲取html文件最少網絡往返)

2、有 JS 和 CSS。鏈接

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Script</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body onload="measureCRP()">
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script src="timing.js"></script>
  </body>
</html>
  • 對比1,增加了 DOMContentLoaded 觸發時間,和 onLoad 很接近了。CSS下載不會阻塞DOM解析,CSS、JS幾乎同時下載。JS會阻塞DOM構建,外部腳本且要等待下載執行完後DOM才能繼續解析。常見的優化是把腳本儘量緊鄰</body>
    在這裏插入圖片描述
  • 關鍵路徑資源:3個(html,js,css)
  • 關鍵路徑資源大小:11KB(html,js,css)
  • 關鍵路徑長度:2次(獲取html文件最少1次。css和js併發下載取其中時間最長的,最少1次網絡往返)
    在這裏插入圖片描述

3、內聯腳本

雖然減少了網絡資源請求,但沒有獲得太大提升,而且不利於公共代碼分享和緩存。CSS樣式下載解析完成之前會阻塞內聯腳本的執行。

  • 關鍵路徑資源:2個(html,css)
  • 關鍵路徑資源大小:11KB(html+內聯js,css)
  • 關鍵路徑長度:2次(獲取html文件最少1次。css最少1次網絡往返)
    在這裏插入圖片描述

4、同時內聯樣式和腳本。鏈接

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Inlined</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <style>
      p { font-weight: bold }
      span { color: red }
      p span { display: none }
      img { float: right }
    </style>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline';  // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>
  • 無阻塞,但HTML文件變大,同樣雖然減少了關鍵資源個數、HTTP請求,但不利於公共樣式、腳本複用和緩存。
    在這裏插入圖片描述

5、有CSS,無JS

<!DOCTYPE html>
<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>

這裏再次說明外聯樣式會阻塞渲染樹生成,CSS應該儘早解析生成CSSOM。

  • 關鍵路徑資源:2個(html,css)
  • 關鍵路徑資源大小:9KB(html,css)
  • 關鍵路徑長度:2次(獲取html文件最少1次。css最少1次網絡往返)在這裏插入圖片描述

6、異步腳本 script

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Async</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body onload="measureCRP()">
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script async src="timing.js"></script>
  </body>
</html>
  • DOMContentLoaded 觸發時間明顯減少,async 表示腳本不阻塞DOM解析,下載完後安排執行。在這裏HTML較輕量,DOM在腳本下載完成之前就解析完畢。等CSS下載解析完成就能合成渲染樹了。
    在這裏插入圖片描述
  • 關鍵路徑資源:2個(html,css)
  • 關鍵路徑資源大小:9KB(html,css)
  • 關鍵路徑長度:2次(獲取html文件最少1次。css最少1次網絡往返)
    在這裏插入圖片描述

7、media CSS、async JS

<!DOCTYPE html>
<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>

<link href="style.css" rel="stylesheet" media="print"> 表示用於打印時渲染的樣式。此時瀏覽器在非打印狀態仍然會下載,但不會阻塞渲染樹生成。這裏由於 HTML DOM樹結構較簡單解析得比較快,async 腳本也沒有阻塞當前DOM構建和渲染。

  • 關鍵路徑資源:1個(html)
  • 關鍵路徑資源大小:5KB(html)
  • 關鍵路徑長度:1次(獲取html文件最少1次)
    在這裏插入圖片描述

defer async 的區別

只對外聯腳本有效

  • defer 下載時不阻塞 HTML 解析成 DOM,下載完成會在DOM解析完畢DOMContentLoaded 事件觸發之前執行,並且保證腳本執行順序。
  • async 下載時不阻塞 HTML 解析成 DOM,下載完畢後儘量安排JS執行。意思說執行時間不確定,早下載早執行。如果 HTML 文件複雜在腳本下載完成還未解析完畢,腳本可能會在 DOMContentLoaded 之前執行阻塞DOM構建。
    在這裏插入圖片描述
    在這裏插入圖片描述

Pre-loading vs Pre-fetching

1、pre-loading
預加載程序不會幹等着 JS 執行阻塞 HTML 解析構建 DOM,會另起線程掃描並下載後面的資源。

  • 關鍵路徑資源:4個(html,css,js x 2)
  • 關鍵路徑長度:2次(獲取html文件最少1次,css和2個JS並行下載計1次)
    在這裏插入圖片描述

2、pre-fetching

  • 預解析域名:
<link rel="dns-prefetch" href="other.hostname.com">
  • 告訴瀏覽器,即將使用資源,要求提高下載優先級:
<link rel="subresource" href="/some_other_resource.js">
  • 獲取下個頁面用到的資源:
<link rel="prefetch" href="/some_other_resource.jpeg">
  • 獲取並渲染頁面:
<link rel="prerender" href="//domain.com/next_page.html">

試題

通過以下執行記錄,大家都能知道應用了那種方式吧。
在這裏插入圖片描述

總結

目的:學習HTML從服務器到瀏覽器後都發生些,知道原理的本質,優化時才能知道瓶頸所在。
在這裏插入圖片描述
優化:減少關鍵渲染路徑資源個數、大小、長度

  1. 壓縮資源減少資源字節數,服務器傳輸開啓GZIP,減少網絡傳輸往返次數和時間。
  2. 樣式阻塞渲染。可以考慮內聯非公共樣式;設置media查詢屬性減少特定功能樣式阻塞關鍵路徑;減少 @import 方式引入,@import 只有下載解析後纔會去獲取樣式文件,不能併發下載,影響性能。
  3. 非構建DOM腳本,使用 async 避免下載阻塞渲染,延遲腳本執行。

參考

視頻課程

分析關鍵渲染路徑性能

w3c performance

預加載

script async defer

Performance Optimization

Inside a super fast CSS engine: Quantum CSS (aka Stylo)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章