導航和資源時間

導航計時是衡量瀏覽器文件導航事件的指標。資源計時是關於應用程式資源載入的詳細網路計時測量。兩者都提供相同的只讀屬性,但導航計時測量主文件的計時,而資源計時提供主文件及其請求的資源所呼叫的所有資產或資源的計時。

以下通用效能計時已被棄用,取而代之的是 Performance Entry API,後者提供了標記和測量導航和資源載入過程中的時間。雖然已棄用,但它們在所有瀏覽器中都受支援。

效能計時

performanceTiming API 是一個用於測量請求頁面載入效能的 JavaScript API,它已被棄用,但在所有瀏覽器中都受支援。它已被 performanceNavigationTiming API 所取代。

效能計時 API 提供只讀時間,以毫秒 (ms) 為單位,描述頁面載入過程中每個點到達的時間。如下圖所示,導航過程從 navigationStartunloadEventStartunloadEventEndredirectStartredirectEndfetchStartdomainLookupStartdomainLookupEndconnectStartconnectEndsecureConnectionStartrequestStartresponseStartresponseEnddomLoadingdomInteractivedomContentLoadedEventStartdomContentLoadedEventEnddomCompleteloadEventStartloadEventEnd 進行。

Navigation Timing event metrics

藉助上述指標和一些計算,我們可以計算許多重要的指標,例如首位元組時間、頁面載入時間、DNS 查詢以及連線是否安全。

為了幫助測量完成所有步驟所需的時間,Performance Timing API 提供了導航計時的只讀測量值。要檢視和捕獲我們應用程式的計時,我們輸入

js
let time = window.performance.timing;

然後我們可以使用結果來衡量我們應用程式的效能。

entering window.performance.timing in the console lists all the timings in the PerformanceNavigationTiming interface

順序是

效能計時 詳情
navigationStart 當同一瀏覽上下文中前一個文件的解除安裝提示終止時。如果沒有前一個文件,此值將與 PerformanceTiming.fetchStart 相同。
secureConnectionStart 當安全連線握手開始時。如果未請求此類連線,則返回 0
redirectStart 當第一次 HTTP 重定向開始時。如果沒有重定向,或者如果其中一個重定向不是同源的,則返回的值為 0
redirectEnd

當最後一個 HTTP 重定向完成時,即當 HTTP 響應的最後一個位元組已收到時。如果沒有重定向,或者如果其中一個重定向不是同源的,則返回的值為 0

connectEnd 當網路連線開啟時。如果傳輸層報告錯誤並重新開始連線建立,則給出上次連線建立結束時間。如果使用持久連線,則該值將與 PerformanceTiming.fetchStart 相同。當所有安全連線握手或 SOCKS 身份驗證終止時,連線被視為已開啟。
connectStart 當向網路傳送開啟連線的請求時。如果傳輸層報告錯誤並重新開始連線建立,則給出上次連線建立開始時間。如果使用持久連線,則該值將與 PerformanceTiming.fetchStart 相同。
domainLookupEnd 當域名查詢完成時。如果使用持久連線,或者資訊儲存在快取或本地資源中,則該值將與 PerformanceTiming.fetchStart 相同。
domainLookupStart 當域名查詢開始時。如果使用持久連線,或者資訊儲存在快取或本地資源中,則該值將與 PerformanceTiming.fetchStart 相同。
fetchStart 當瀏覽器準備好使用 HTTP 請求獲取文件時。此時刻在檢查任何應用程式快取之前
requestStart 當瀏覽器傳送請求以從伺服器或快取獲取實際文件時。如果請求開始後傳輸層失敗並重新開啟連線,則此屬性將設定為與新請求對應的時間。
responseStart 當瀏覽器從伺服器、快取或本地資源收到響應的第一個位元組時。
responseEnd 當瀏覽器收到響應的最後一個位元組時,或者如果連線在此之前關閉,則從伺服器、快取或本地資源收到。
domLoading 當解析器開始工作時,即當其 Document.readyState 更改為 'loading' 並丟擲相應的 readystatechange 事件時。
unloadEventStart unload 事件被丟擲時,表示視窗中前一個文件開始解除安裝的時間。如果沒有前一個文件,或者如果前一個文件或所需的重定向之一不是同源的,則返回的值為 0
unloadEventEnd unload 事件處理程式完成時。如果沒有前一個文件,或者如果前一個文件或所需的重定向之一不是同源的,則返回的值為 0
domInteractive 當解析器完成對主文件的工作時,即當其 Document.readyState 更改為 'interactive' 並丟擲相應的 readystatechange 事件時。
domContentLoadedEventStart 在解析器傳送 DOMContentLoaded 事件之前,即在所有需要在解析後立即執行的指令碼都已執行之後。
domContentLoadedEventEnd 在所有需要儘快執行的指令碼,無論順序如何,都已執行之後。
domComplete 當解析器完成對主文件的工作時,即當其 Document.readyState 更改為 'complete' 並丟擲相應的 readystatechange 事件時。
loadEventStart load 事件為當前文件傳送時。如果此事件尚未傳送,則返回 0
loadEventEnd load 事件處理程式終止時,即當載入事件完成時。如果此事件尚未傳送或尚未完成,則返回 0

計算計時

我們可以使用這些值來測量感興趣的特定計時

js
const dns = time.domainLookupEnd - time.domainLookupStart;
const tcp = time.connectEnd - time.connectStart;
const tls = time.requestStart - time.secureConnectionStart;

首位元組時間

首位元組時間 (Time to First Byte)navigationStart(導航開始)和 responseStart(收到響應的第一個位元組)之間的時間,可在 performanceTiming API 中獲取

js
const ttfb = time.responseStart - time.navigationStart;

頁面載入時間

頁面載入時間navigationStart 和當前文件的載入事件開始傳送之間的時間。它們僅在 performanceTiming API 中可用。

js
let pageloadTime = time.loadEventStart - time.navigationStart;

DNS 查詢時間

DNS 查詢時間是 domainLookupStartdomainLookupEnd 之間的時間。這些在 performanceTimingperformanceNavigationTiming API 中都可用。

js
const dns = time.domainLookupEnd - time.domainLookupStart;

TCP

TCP 握手所需的時間是連線開始和連線結束之間的時間

js
const tcp = time.connectEnd - time.connectStart;

TLS 協商

如果不可用,secureConnectionStart 將為 undefined;如果未使用 HTTPS,則為 0;如果可用且已使用,則為時間戳。換句話說,如果使用了安全連線,secureConnectionStart 將為 真值,並且 secureConnectionStartrequestStart 之間的時間將大於 0。

js
const tls = time.requestStart - time.secureConnectionStart;

Performance Entry API

上述通用效能計時已棄用但完全受支援。我們現在有 Performance Entry API,它提供了標記和測量導航和資源載入過程中的時間。您還可以建立標記

js
performance.getEntriesByType("navigation").forEach((navigation) => {
  console.dir(navigation);
});

performance.getEntriesByType("resource").forEach((resource) => {
  console.dir(resource);
});

performance.getEntriesByType("mark").forEach((mark) => {
  console.dir(mark);
});

performance.getEntriesByType("measure").forEach((measure) => {
  console.dir(measure);
});

performance.getEntriesByType("paint").forEach((paint) => {
  console.dir(paint);
});

performance.getEntriesByType("frame").forEach((frame) => {
  console.dir(frame);
});

在支援的瀏覽器中,您可以使用 performance.getEntriesByType('paint') 查詢 first-paintfirst-contentful-paint 的測量值。我們使用 performance.getEntriesByType('navigation')performance.getEntriesByType('resource') 分別查詢導航和資源計時。

當用戶請求網站或應用程式時,為了填充瀏覽器,使用者代理會經歷一系列步驟,包括 DNS 查詢、TCP 握手和 TLS 協商,然後使用者代理發出實際請求,伺服器返回請求的資源。然後瀏覽器解析收到的內容,構建 DOM、CSSOM、可訪問性和渲染樹,最終渲染頁面。一旦使用者代理停止解析文件,使用者代理會將文件就緒狀態設定為互動式。如果有需要解析的延遲指令碼,它將進行解析,然後觸發 DOMContentLoaded 事件,之後就緒狀態設定為完成。文件現在可以處理載入後任務,之後文件被標記為完全載入。

js
const navigationTimings = performance.getEntriesByType("navigation");

performance.getEntriesByType('navigation') 返回一個針對導航型別PerformanceEntry 物件陣列。

The results of when performance.getEntriesByType('navigation'); is entered into the console for this document

可以從這些計時中獲得很多資訊。在上圖中,我們透過 name 屬性看到被計時檔案是此文件。對於本解釋的其餘部分,我們使用以下變數

js
const timing = performance.getEntriesByType("navigation")[0];

協議

我們可以透過查詢來檢查使用的協議

js
const protocol = timing.nextHopProtocol;

它返回用於獲取資源的網路協議:在本例中,http/2h2

壓縮

要獲得壓縮節省百分比,我們將 transferSize 除以 decodedBodySize,然後從 100% 中減去。我們看到節省了超過 74%。

js
const compressionSavings = 1 - timing.transferSize / timing.decodedBodySize;

我們本可以使用

js
const compressionSavings = 1 - timing.encodedBodySize / timing.decodedBodySize;

但使用 transferSize 會包含開銷位元組。

為了進行比較,我們可以檢視網路選項卡,發現我們傳輸了 22.04KB,而未壓縮檔案大小為 87.24KB。

View of the bytes transferred and the size via the network tab

如果我們用這些數字計算,我們會得到相同的結果:1 - (22.04 / 87.24) = 0.747。導航計時為我們提供了一種以程式設計方式檢查傳輸大小和頻寬節省的方法。

請注意,這僅是此單個文件的大小:僅針對此資源,而不是所有資源的總和。然而,持續時間、載入事件和與 DOM 相關的計時與整個導航有關,而不是此單個資產。客戶端 Web 應用程式可能看起來比此應用程式更快,傳輸大小小於 10000,解碼主體大小小於 30000,但這並不意味著 JavaScript、CSS 或媒體資產沒有增加膨脹。檢查壓縮率很重要,但也要確保檢查持續時間以及 DOMContentLoaded 事件結束到 DOM 完成之間的時間,因為長時間在主執行緒上執行 JavaScript 會導致使用者介面無響應。

請求時間

API 並未提供您可能需要的每個測量值。例如,請求花了多長時間?我們可以利用現有的測量值來獲得答案。

要測量響應時間,請從響應開始時間中減去請求開始時間。請求開始時間是指使用者代理開始從伺服器、相關應用程式快取或本地資源請求資源之前的時刻。響應開始時間是指使用者代理的 HTTP 解析器從相關應用程式快取、本地資源或伺服器收到響應的第一個位元組之後的時刻,這發生在請求收到並處理之後。

js
const request = timing.responseStart - timing.requestStart;

載入事件持續時間

透過從當前文件載入事件完成的時間中減去當前文件載入事件觸發前的精確時間戳,您可以測量載入事件的持續時間。

js
const load = timing.loadEventEnd - timing.loadEventStart;

DOMContentLoaded 事件

DOMContentLoaded 事件的持續時間是透過從事件完成後的時間值中減去使用者代理觸發 DOMContentLoaded 事件前的精確時間值來測量的。將其保持在 50 毫秒或更快有助於確保響應式使用者介面。

js
const DOMContentLoaded =
  timing.domContentLoadedEventEnd - timing.domContentLoadedEventStart;

持續時間

我們獲得了持續時間。持續時間是 PerformanceNavigationTiming.loadEventEndPerformanceEntry.startTime 屬性之間的差值。

PerformanceNavigationTiming 介面還提供了關於您正在測量的導航型別的資訊,返回 navigatereloadback_forward

資源計時

導航計時用於測量主頁面的效能,通常是請求所有其他資產的 HTML 檔案,而資源計時則測量單個資源的計時,即主頁面呼叫的資產,以及這些資源請求的任何資產。許多測量是相似的:有 DNS 查詢、TCP 握手和每個域執行一次的安全連線開始。

Graphic of Resource Timing timestamps

每個資源需要關注的主要事項。

另見