如何使用 Navigation Timing 和 Resource Timing 評估欄位中的載入效能

瞭解使用 Navigation 和 Resource Timing API 來評估欄位載入效能的基本概念。

發布日期:2021 年 10 月 8 日

如果您曾使用瀏覽器開發人員工具中的網路面板中 (或 Chrome 的 Lighthouse) 評估載入效能,就表示這些工具方便您調整效能。你可以透過穩定一致的基準連線速度,快速評估效能最佳化設定的影響。唯一的問題在於這是合成測試,因此會產生研究室資料,而非現場資料

合成測試本身並非不好,但無法代表網站對實際使用者來說載入速度有多快。這需要欄位資料,可從 Navigation Timing 和 Resource Timing API 收集。

有助於評估領域內載入效能的 API

Navigation Timing 和 Resource Timing 是兩個相似的 API,兩者有許多重疊之處,但測量項目不同:

  • Navigation Timing 可評估 HTML 文件 (亦即導覽要求) 要求的速度。
  • 資源時間會評估文件相關資源 (例如 CSS、JavaScript、圖片和其他資源類型) 的請求速度。

這些 API 會在效能輸入緩衝區中公開資料,您可以透過 JavaScript 在瀏覽器中存取。您可以透過多種方式查詢效能緩衝區,但常見的方式是使用 performance.getEntriesByType

// Get Navigation Timing entries:
performance.getEntriesByType('navigation');

// Get Resource Timing entries:
performance.getEntriesByType('resource');

performance.getEntriesByType 接受一個字串,用來說明您要從效能項目緩衝區擷取的項目類型。'navigation''resource' 可分別擷取 Navigation Timing 和 Resource Timing API 的時間。

這些 API 提供的資訊可能會讓人吃緊,但是評估欄位中載入效能的關鍵,因為您可以從使用者造訪網站時收集時間。

網路要求的生命週期和時間

收集及分析導覽和資源時間點,有點像是考古學,因為您是在事後重建網路要求的短暫生命週期。有時候,以視覺化方式呈現概念有助於理解概念,而瀏覽器的開發人員工具則可協助您瞭解網路要求。

Chrome 開發人員工具中顯示的網路時間。圖表中顯示的時間是要求排隊、連線協商、要求本身和回應的時間,並以不同顏色的長條表示。
Chrome 開發人員工具網路面板中的網路要求視覺化

網路要求的生命週期包含不同的階段,例如 DNS 查詢、連線建立、TLS 協商,以及其他延遲來源。這些時間會以 DOMHighResTimestamp 表示。時間精細程度可能會因瀏覽器而異,可能以微秒為單位,或四捨五入至毫秒。建議您詳細檢查這些階段,以及各階段與 Navigation Timeing 和資源 Timeming 之間的關聯。

DNS 查詢

使用者造訪網址時,系統會查詢網域名稱系統 (DNS),將網域轉譯為 IP 位址。這項程序可能會花費大量時間,您甚至可能需要在現場測量。Navigation 時間與資源計時可顯示兩種 DNS 相關時間:

  • domainLookupStart 是 DNS 查詢開始的時間。
  • domainLookupEnd 是 DNS 查詢結束時間。

如要計算 DNS 查詢總時間,請將結束指標減去開始指標:

// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;

連線協商

連線協商是影響載入效能的另一個因素,這是指連線至網路伺服器時的延遲時間。如果涉及 HTTPS,這項程序也會納入傳輸層安全標準 (TLS) 交涉時間。連結階段包含三種時間:

  • connectStart 是瀏覽器開始開啟連至網路伺服器的連線時。
  • secureConnectionStart 會標示用戶端開始 TLS 交涉的時間。
  • connectEnd 是已建立與網路伺服器的連線。

總連線時間的測量方式與評估 DNS 查詢總時間類似,也就是將開始時間減去開始時間。不過,如未使用 HTTPS 或持續連線,則其他 secureConnectionStart 屬性可能會是 0。如要評估 TLS 協商時間,請注意以下事項:

// Quantifying total connection time
const [pageNav] = performance.getEntriesByType('navigation');
const connectionTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0; // <-- Assume 0 to start with

// Was there TLS negotiation?
if (pageNav.secureConnectionStart > 0) {
  // Awesome! Calculate it!
  tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}

當 DNS 查詢和連線交涉結束後,與擷取文件及其相依資源相關的時間就會派上用場。

要求與回應

載入效能會受到以下兩種因素影響:

  • 極端因素:延遲時間和頻寬等。除了選擇代管公司和 CDN 以外,由於使用者無論身在何處,他們都可以存取網路,因此這些公司大部分都無法掌控。
  • 內建因素:伺服器和用戶端架構、資源大小,以及我們能否針對這些項目進行最佳化,這些因素都是由我們自行控管。

這兩種因素都會影響載入效能。與這些因素相關的時間點非常重要,因為這些時間點說明資源下載所需的時間。導覽時機和資源時機都會使用下列指標說明載入效能:

  • fetchStart 標示瀏覽器開始擷取資源 (資源時間) 或導覽要求的文件 (導覽時間) 的時間點。這會在實際要求之前加上,同時也是瀏覽器檢查快取的時間點 (例如 HTTP 和 Cache 執行個體)。
  • workerStart 會標示服務工作元的 fetch 事件處理常式何時開始處理要求。如果沒有 Service Worker 控管目前的網頁,這個值會是 0
  • requestStart 是瀏覽器發出要求的時間。
  • responseStart 是回應的第一個位元組到達時。
  • responseEnd 是收到回應的最後一個位元組。

這些時間可讓您評估多個載入效能的層面,例如服務工作站中的快取查詢「和」下載時間:

// Cache seek plus response time of the current document
const [pageNav] = performance.getEntriesByType('navigation');
const fetchTime = pageNav.responseEnd - pageNav.fetchStart;

// Service worker time plus response time
let workerTime = 0;

if (pageNav.workerStart > 0) {
  workerTime = pageNav.responseEnd - pageNav.workerStart;
}

您也可以評估要求和回應延遲時間的其他層面:

const [pageNav] = performance.getEntriesByType('navigation');

// Request time only (excluding redirects, DNS, and connection/TLS time)
const requestTime = pageNav.responseStart - pageNav.requestStart;

// Response time only (download)
const responseTime = pageNav.responseEnd - pageNav.responseStart;

// Request + response time
const requestResponseTime = pageNav.responseEnd - pageNav.requestStart;

評估的其他指標

除了上述範例所述的用途之外,Navigation 時間和 Resource 時間也非常實用。以下是其他可能值得探討的相關時間點:

  • 網頁重新導向:重新導向是導致延遲時間增加的常見原因,尤其是重新導向鏈結。系統會透過多種方式增加延遲時間,例如 HTTP 到 HTTP 躍點,以及 302/未快取的 301 重新導向。redirectStartredirectEndredirectCount 時間有助於評估重新導向延遲。
  • 卸載文件:unload 事件處理常式中執行程式碼的網頁中,瀏覽器必須先執行該程式碼,才能前往下一頁。unloadEventStartunloadEventEnd 會測量文件卸載情形。
  • 文件處理:除非網站傳送的 HTML 酬載非常大,否則文件處理時間可能不會造成影響。如果您的情況符合這項描述,就可能想瞭解domInteractivedomContentLoadedEventStartdomContentLoadedEventEnddomComplete的顯示時間。

如何取得程式碼顯示時間

目前顯示的所有範例都使用 performance.getEntriesByType,但還有其他方式可以查詢效能項目緩衝區,例如 performance.getEntriesByNameperformance.getEntries。如果只需要進行簡單的分析,這些方法就很適合。不過,在其他情況下,這些方法可能會透過重複處理大量項目,甚至重複輪詢效能緩衝區來尋找新項目,導致主執行緒工作過度。

如要從效能項目緩衝區收集項目,建議您使用 PerformanceObserverPerformanceObserver 會監聽成效項目,並在項目加入緩衝區時提供這些項目:

// Create the performance observer:
const perfObserver = new PerformanceObserver((observedEntries) => {
  // Get all resource entries collected so far:
  const entries = observedEntries.getEntries();

  // Iterate over entries:
  for (let i = 0; i < entries.length; i++) {
    // Do the work!
  }
});

// Run the observer for Navigation Timing entries:
perfObserver.observe({
  type: 'navigation',
  buffered: true
});

// Run the observer for Resource Timing entries:
perfObserver.observe({
  type: 'resource',
  buffered: true
});

與直接存取效能輸入緩衝區相比,這種收集時間方法可能會感到困惑,但建議在主執行緒中加入不重要且面向使用者的工作。

如何手機回家

收集所需的所有時間後,您可以將這些時間傳送至端點,以便進一步分析。您可以使用 navigator.sendBeaconfetch (已設定 keepalive 選項) 來執行這項操作。這兩種方法都會以非封鎖的方式傳送要求至指定端點,而在下列情況中,請求會以超過目前頁面工作階段的方式排入佇列:

// Check for navigator.sendBeacon support:
if ('sendBeacon' in navigator) {
  // Caution: If you have lots of performance entries, don't
  // do this. This is an example for illustrative purposes.
  const data = JSON.stringify(performance.getEntries());

  // Send the data!
  navigator.sendBeacon('/analytics', data);
}

在這個範例中,JSON 字串會傳送至 POST 酬載,您可以視需要對其進行解碼、處理,並儲存在應用程式後端。

結論

當您收集到指標後,就可以決定如何分析該欄位資料。分析現場資料時,請留意以下幾項通則,以確保得出的結論有意義:

  • 避免使用平均值,因為這類指標無法代表任何使用者的使用體驗,而且可能有異常值。
  • 依據百分位數。在以時間為基礎的成效指標資料集中,效能越低越好。換句話說,如果您優先處理較低的百分位數,只會注意最快的體驗。
  • 優先處理長尾值。如果將體驗的優先順序放在第 75 個百分位數以上,您就會將重點放在最慢的部分,也就是速度最慢的體驗。

本指南並非詳盡的導覽或資源計時資源,而是起點。以下提供幾項額外的實用資源:

有了這些 API 和其提供的資料,您會更加瞭解實際使用者的載入效能,更放心地診斷和解決領域中的載入效能問題。