自訂指標

以使用者為中心的指標可在任何網站上普遍評估,因此非常有用。這些指標可讓您:

  • 瞭解實際使用者對整個網際網路的體驗。
  • 比較您的網站與競爭對手的網站。
  • 在數據分析工具中追蹤實用且可採取行動的資料,無須編寫自訂程式碼。

通用指標可提供良好的基準,但在許多情況下,您不僅必須多評估這些指標,,才能掌握特定網站的完整體驗。

您可以使用自訂指標評估網站體驗的某些面向,這些面向可能只適用於您的網站,例如:

  • 單頁應用程式 (SPA) 從一個「頁面」轉換至另一個「頁面」所需的時間。
  • 網頁顯示從資料庫擷取的資料,所需的時間長度 (以秒為單位)。
  • 伺服器端算繪 (SSR) 應用程式重新整理所需的時間。
  • 回訪者載入資源的快取命中率。
  • 遊戲中點擊或鍵盤事件的事件延遲時間。

用於評估自訂指標的 API

過去,網頁程式開發人員沒有太多低階 API 可用來評估成效,因此必須採取駭客手段,才能評估網站效能是否良好。

舉例來說,您可以執行 requestAnimationFrame 迴圈,並計算每個影格之間的差異,藉此判斷主執行緒是否因長時間執行的 JavaScript 工作而遭到封鎖。如果差異遠大於顯示器的影格速率,您可以將差異回報為長時間的工作。不過,我們不建議使用這類駭客攻擊,因為這類攻擊會影響效能 (例如耗盡電池電量)。

有效評估效能的第一個原則,就是確保效能評估技術本身不會造成效能問題。因此,建議您針對網站上評估的所有自訂指標,盡可能使用下列其中一個 API。

Performance Observer API

瀏覽器支援

  • Chrome:52。
  • Edge:79。
  • Firefox:57。
  • Safari:11。

資料來源

Performance Observer API 是一種機制,可收集並顯示本頁所述其他所有效能 API 的資料。如要取得優質資料,就必須瞭解這項原則。

您可以使用 PerformanceObserver 被動訂閱與成效相關的事件。這可讓 API 回呼在閒置期間觸發,也就是說,回呼通常不會影響網頁效能。

如要建立 PerformanceObserver,請傳遞回呼,以便在每次調度新的成效項目時執行。接著,您將使用 observe() 方法告知觀察器要監聽的項目類型:

const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

po.observe({type: 'some-entry-type'});

下列各節會列出可供觀察的所有項目類型,但在較新的瀏覽器中,您可以透過靜態 PerformanceObserver.supportedEntryTypes 屬性檢查可用的項目類型。

觀察已發生的項目

根據預設,PerformanceObserver 物件只能在發生項目時觀測項目。如果您想延後載入效能分析程式碼,以免阻斷優先順序較高的資源,這可能會導致問題。

如要取得歷史記錄項目 (發生後),請在呼叫 observe() 時將 buffered 標記設為 true。首次呼叫 PerformanceObserver 回呼時,瀏覽器會納入效能項目緩衝區中的歷史項目,直到該類型的緩衝區大小上限

po.observe({
  type: 'some-entry-type',
  buffered: true,
});

應避免使用的舊版效能 API

在 Performance Observer API 之前,開發人員可以使用以下三種 performance 物件中定義的方法存取效能項目:

雖然這些 API 仍受支援,但我們不建議使用這些 API,因為您無法透過這些 API 監聽新項目何時產生。此外,許多新的 API (例如 largest-contentful-paint) 並未透過 performance 物件公開,而是只透過 PerformanceObserver 公開。

除非您需要與 Internet Explorer 相容,否則最好避免在程式碼中使用這些方法,並改用 PerformanceObserver

User Timing API

瀏覽器支援

  • Chrome:28。
  • Edge:12.
  • Firefox:38。
  • Safari:11。

資料來源

User Timing API 是針對時間指標的一般用途評估 API,可讓您任意在時間點標記點,之後測量這些標記之間的持續時間。

// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();

// Record the time immediately after running a task.
performance.mark('myTask:end');

// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');

雖然 Date.now()performance.now() 等 API 可提供類似功能,但 User Timing API 卻能與效能工具完美整合。舉例來說,Chrome 開發人員工具會在「效能」面板中以視覺化方式呈現使用者時間測量結果,許多數據分析供應商也會自動追蹤您所做的任何測量,並將時間長度資料傳送至其數據分析後端。

如要回報使用者時間測量結果,您可以使用 PerformanceObserver,並註冊觀察 measure 類型項目:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});

Long Tasks API

瀏覽器支援

  • Chrome:58。
  • Edge:79。
  • Firefox:不支援。
  • Safari:不支援。

資料來源

Long Tasks API 可用於瞭解瀏覽器主執行緒遭到阻斷的時間長度,以便判斷是否會影響影格速率或輸入延遲時間。API 會回報執行時間超過 50 毫秒的任何工作。

每當您需要執行耗用大量資源的程式碼,或載入及執行大型指令碼時,追蹤該程式碼是否會阻斷主執行緒就很有幫助。事實上,許多較高層級的指標都是以 Long Tasks API 本身為基礎 (例如互動準備時間 (TTI)總阻斷時間 (TBT))。

如要判斷長時間工作任務何時發生,您可以使用 PerformanceObserver,並註冊觀察 longtask 類型項目:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});

Long Animation Frames API

瀏覽器支援

  • Chrome:123。
  • Edge:123。
  • Firefox:不支援。
  • Safari:不支援。

資料來源

Long Animation Frames API 是 Long Tasks API 的新疊代作業,依據超過 50 毫秒的「長影格」(而非「長時間工作」)。這項功能可解決 Long Tasks API 的部分缺點,包括提供更精確的歸因資訊,以及更廣泛的潛在延遲問題範圍。

如要判斷影格發生時間,可以使用 PerformanceObserver 並註冊,觀察 long-animation-frame 類型的項目:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});

元素時間 API

瀏覽器支援

  • Chrome:77。
  • Edge:79。
  • Firefox:不支援。
  • Safari:不支援。

資料來源

「Largest Contentful Paint (LCP)」指標可用來瞭解系統何時繪製最大的圖片或文字區塊,但在某些情況下,還是需要測量不同元素的轉譯時間。

在這種情況下,請使用 Element Timing API。LCP API 實際上是建構在 Element Timing API 之上,並會自動回報最大的含有內容元素,但您也可以在其他元素中明確加入 elementtiming 屬性,並註冊 PerformanceObserver 來觀察 element 項目類型,以便回報其他元素。

<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->

<script>
  const po = new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  // Start listening for `element` entries to be dispatched.
  po.observe({type: 'element', buffered: true});
</script>

事件時間 API

瀏覽器支援

  • Chrome:76。
  • Edge:79。
  • Firefox:89。
  • Safari:不支援。

資料來源

Interaction to Next Paint (INP) 指標會觀察網頁生命週期內的所有點擊、輕觸和鍵盤互動,評估網頁的整體回應速度。網頁 INP 通常是耗時最長的互動時間,從使用者啟動互動開始,到瀏覽器繪製下一個影格 (顯示使用者輸入的影像結果) 所需的時間。

INP 指標是由 Event Timing API 提供。這個 API 會顯示事件生命週期內發生的幾種時間戳記,包括:

  • startTime:瀏覽器收到事件的時間。
  • processingStart:瀏覽器可開始處理事件事件處理常式時的時間。
  • processingEnd:瀏覽器完成執行此事件事件處理常式啟動的所有同步程式碼的時間。
  • duration:從瀏覽器收到事件開始,到執行事件處理常式啟動的所有同步程式碼後,直到繪製下一個影格為止所需的時間長度 (以 8 毫秒為單位)。

以下範例說明如何使用這些值建立自訂評估指標:

const po = new PerformanceObserver((entryList) => {
  // Get the last interaction observed:
  const entries = Array.from(entryList.getEntries()).forEach((entry) => {
    // Get various bits of interaction data:
    const inputDelay = entry.processingStart - entry.startTime;
    const processingTime = entry.processingEnd - entry.processingStart;
    const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
    const duration = entry.duration;
    const eventType = entry.name;
    const target = entry.target || "(not set)"

    console.log("----- INTERACTION -----");
    console.log(`Input delay (ms): ${inputDelay}`);
    console.log(`Event handler processing time (ms): ${processingTime}`);
    console.log(`Presentation delay (ms): ${presentationDelay}`);
    console.log(`Total event duration (ms): ${duration}`);
    console.log(`Event type: ${eventType}`);
    console.log(target);
  });
});

// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});

Resource Timing API

瀏覽器支援

  • Chrome:29.
  • Edge:12.
  • Firefox:35。
  • Safari:11。

資料來源

Resource Timing API 可讓開發人員深入瞭解特定網頁的資源載入方式。雖然這個 API 的名稱是「Timing」,但提供的資訊不限於時間資料 (雖然有許多這類資料)。您可以存取的其他資料包括:

  • initiatorType:資源的擷取方式,例如從 <script><link> 標記,或是從 fetch() 呼叫擷取。
  • nextHopProtocol:用來擷取資源的通訊協定,例如 h2quic
  • encodedBodySize/decodedBodySize]:以編碼或解碼格式表示的資源大小 (分別指定)
  • transferSize:實際透過網路轉移的資源大小。如果資源是由快取滿足,這個值可能會比 encodedBodySize 小得多,在某些情況下甚至可能為零 (如果不需要重新驗證快取)。

您可以使用資源時間資訊項目的 transferSize 屬性,評估快取命中率指標或快取資源總大小指標,這有助於瞭解資源快取策略對回訪訪客成效的影響。

以下範例會記錄網頁要求的所有資源,並指出快取是否已完成各項資源。

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log(entry.name, entry.transferSize === 0);
  }
});

// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});

瀏覽器支援

  • Chrome:57。
  • 邊緣:12。
  • Firefox:58。
  • Safari:15。

資料來源

Navigation Timing API 與 Resource Timing API 類似,但只會回報導覽要求navigation 輸入類型也與 resource 輸入類型相似,但包含一些專屬於導覽要求的額外資訊 (例如 DOMContentLoadedload 事件觸發時的資訊)。

許多開發人員會追蹤這項指標,以瞭解伺服器回應時間 (第 1 個位元組時間 (TTFB)),您可以使用 Navigation Timing API 查看這項指標,具體來說,就是項目的 responseStart 時間戳記。

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log('Time to first byte', entry.responseStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

使用 Service Worker 的開發人員可能會關心另一個指標,也就是導覽要求的 Service Worker 啟動時間。瀏覽器啟動服務工作者執行緒所需的時間,之後才能開始攔截擷取事件。

您可以根據 entry.responseStartentry.workerStart 之間的差異,判斷特定導覽要求的服務工作站啟動時間。

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Service Worker startup time:',
        entry.responseStart - entry.workerStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

Server Timing API

瀏覽器支援

  • Chrome:65。
  • Edge:79。
  • Firefox:61。
  • Safari:16.4。

資料來源

Server Timing API 可讓您透過回應標頭,將要求專屬的時間資料從伺服器傳遞至瀏覽器。例如,您可以指出針對特定要求查詢資料庫中的資料花費的時間,這在偵錯因伺服器速度緩慢而造成的效能問題時很有用。

如果開發人員使用第三方數據分析供應商,則只有使用 Server Timing API,才能將伺服器效能資料與這些數據分析工具可能評估的其他業務指標建立關聯。

如要在回應中指定伺服器時間資料,您可以使用 Server-Timing 回應標頭。以「Black cat ate the mouse」

HTTP/1.1 200 OK

Server-Timing: miss, db;dur=53, app;dur=47.2

接著,您可以從網頁中讀取 Resource Timing 和 Navigation Timing API 的 resourcenavigation 項目資料。

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Logs all server timing data for this response
    console.log('Server Timing', entry.serverTiming);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});