自訂指標

採用以使用者為中心的通用指標,任何網站上皆可評估,這些指標也有助於瞭解使用者對網路的體驗,以及您的網站與競爭對手的比較。不過在許多情況下,您需要評估的不只是通用指標,才能獲得特定網站的完整體驗。

自訂指標可讓您評估網站體驗可能僅適用於網站的各個層面,例如:

  • 單頁應用程式 (SPA) 從一個「頁面」轉移至另一個「頁面」所需的時間。
  • 網頁顯示從資料庫擷取的資料 (用於已登入的使用者) 需要多久時間。
  • 伺服器端算繪 (SSR) 應用程式飲水所需的時間。
  • 回訪者載入的資源快取命中率。
  • 遊戲中點選或鍵盤事件的事件延遲。

用來評估自訂指標的 API

網頁程式開發人員過去並未擁有許多低階 API 來衡量效能,因此必須設法進行入侵,才能評估網站效能是否良好。舉例來說,您可以執行 requestAnimationFrame 迴圈並計算每個影格之間的差異,判斷主要執行緒是否因長時間執行的 JavaScript 工作而遭到封鎖。如果差異值遠大於螢幕影格速率,您可以回報長時間的工作。

不過,這類入侵行為可能會影響網站的效能,例如削減裝置的電池電量。如果您的效能評估技術本身會造成效能問題,從它們取得的資料就會是不準確的。因此,我們建議您使用下列任一 API 來建立自訂指標。

Performance Observer API

瀏覽器支援

  • 52
  • 79
  • 57
  • 11

資料來源

Performance Observer API 是一種機制,可從本頁討論的所有其他效能 API 收集並顯示資料。瞭解 才能取得優質資料至關重要

您可以使用 PerformanceObserver 被動訂閱效能相關事件。這允許 API 回呼在閒置期間觸發,這表示通常不會幹擾網頁效能。

在建立 PerformanceObserver 時向其傳遞回呼,每當有新的效能項目時就會執行。接著,使用 observe() 方法向觀察器指示要監聽的項目類型,如下所示:

// Catch errors that some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  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'});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

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

觀察已顯示的項目

根據預設,PerformanceObserver 物件只能觀測發生的項目。如果您想延遲載入效能分析程式碼,這樣就不會封鎖優先順序較高的資源。

如要取得記錄項目,請呼叫 observe,並將 buffered 旗標設為 true。然後,瀏覽器會在第一次呼叫 PerformanceObserver 回呼時,納入效能項目緩衝區的歷史項目。

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

應避免使用舊版效能 API

在 Performance Observer API 之前,開發人員可以使用下列針對 performance 物件定義的方法存取效能項目。但我們不建議您使用,因為這樣無法監聽新作品。

此外,許多新的 API (例如長工作) 不會由 performance 物件公開,只有 PerformanceObserver 會公開。因此,除非您特別需要與 Internet Explorer 相容,否則最好避免在程式碼中使用這類方法,而日後再使用 PerformanceObserver

使用者載入時間 API

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 開發人員工具以「Performance」(效能) 面板以視覺化方式呈現使用者時間測量結果,許多分析供應商會自動追蹤您的任何測量結果,並將持續時間資料傳送至其數據分析後端。

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

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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.
  po.observe({type: 'measure', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Long Tasks API

瀏覽器支援

  • 58
  • 79
  • x
  • x

資料來源

Long Tasks API 可用於判斷瀏覽器的主要執行緒何時遭到封鎖的時間長到會影響影格速率或輸入延遲時間。API 會回報執行時間超過 50 毫秒 (毫秒) 的所有工作。

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

如要判斷長時間工作發生的時間,請註冊 PerformanceObserver 以觀察 longtask 類型的項目:

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

元素時間 API

瀏覽器支援

  • 77
  • 79
  • x
  • x

資料來源

透過最大內容繪製 (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>
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
</script>

事件時間 API

「首次輸入延遲時間 (FID)」指標可測量從使用者首次與網頁互動,到瀏覽器可以開始處理事件處理常式以回應該互動起所需的時間。不過,在某些情況下,自行評估事件處理時間也很有幫助。

您可以使用 Event Timing API 來達成這個目的,它除了評估 FID 之外,也會在事件生命週期中顯示許多時間戳記,包括:

  • startTime:瀏覽器收到事件的時間。
  • processingStart:瀏覽器可開始處理事件事件處理常式的時間。
  • processingEnd:瀏覽器完成從事件處理常式啟動的所有同步程式碼的時間。
  • duration:從事件處理常式啟動完所有同步程式碼後,從瀏覽器收到事件到可繪製下一個影格所需的時間 (四捨五入至 8 毫秒)。

以下範例說明如何使用這些值建立自訂測量:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((entryList) => {
    const firstInput = entryList.getEntries()[0];

    // Measure First Input Delay (FID).
    const firstInputDelay = firstInput.processingStart - firstInput.startTime;

    // Measure the time it takes to run all event handlers
    // Doesn't include work scheduled asynchronously using methods like
    // `requestAnimationFrame()` or `setTimeout()`.
    const firstInputProcessingTime = firstInput.processingEnd - firstInput.processingStart;

    // Measure the entire duration of the event, from when input is received by
    // the browser until the next frame can be painted after processing all
    // event handlers.
    // Doesn't include work scheduled asynchronously using
    // `requestAnimationFrame()` or `setTimeout()`.
    // For security reasons, this value is rounded to the nearest 8 ms.
    const firstInputDuration = firstInput.duration;

    // Log these values to the console.
    console.log({
      firstInputDelay,
      firstInputProcessingTime,
      firstInputDuration,
    });
  });

  po.observe({type: 'first-input', buffered: true});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}

資源時間 API

Resource Timing API 可針對特定網頁的資源載入方式提供詳盡的深入分析。儘管有 API 名稱,但它提供的資訊並非只有時間資料 (但仍有很多資料)。您可以存取的其他資料包括:

  • initiatorType:擷取資源的方式,例如透過 <script><link> 標記或 fetch()
  • nextHopProtocol:用來擷取資源的通訊協定,例如 h2quic
  • encodedBodySizedecodedBodySize]:資源的編碼或解碼格式 (分別)。
  • transferSize:實際透過網路傳輸的資源大小。使用快取完成資源時,這個值可能遠小於 encodedBodySize,在某些情況下,如果不需要重新驗證,這個值會是零。

您可以使用資源時間項目的 transferSize 屬性,測量「快取命中率」指標或「快取資源大小總計」指標,以便瞭解資源快取策略對回訪者效能的影響。

下列範例會記錄該網頁要求的所有資源,並指出每項資源是否已透過快取完成:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log(entry.name, entry.transferSize === 0);
    }
  });
  // Start listening for `resource` entries to be dispatched.
  po.observe({type: 'resource', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

瀏覽器支援

  • 57
  • 12
  • 58
  • 15

資料來源

Navigation Timing API 與 Resource Timing API 類似,但只會回報導覽要求navigation 項目類型也類似於 resource 項目類型,但包含部分僅適用於導覽要求 (例如 DOMContentLoadedload 事件觸發時) 特有的其他資訊

許多開發人員為了瞭解伺服器回應時間 (First Byte (TTFB)) 而追蹤的指標,可透過 Navigation Timing API 中的 responseStart 時間戳記取得。

// Catch errors since  browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

使用服務工作站的另一個指標開發人員可能在乎導覽要求的 Service Worker 啟動時間。瀏覽器啟動 Service Worker 執行緒之前,要經過多久時間才會開始攔截擷取事件。

您可從 entry.responseStartentry.workerStart 之間的差異判斷特定導覽要求的 Service Worker 啟動時間,如下所示:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

伺服器時間 API

Server Timing API 可讓您使用回應標頭,將要求的特定時間資料從伺服器傳送至瀏覽器。例如,您可以指定針對特定要求在資料庫中查詢資料所需的時間,這對於因伺服器速度緩慢而造成的效能問題進行偵錯。

對於使用第三方分析供應商的開發人員而言,Server Timing API 是為伺服器效能資料與這些分析工具測量的其他業務指標建立關聯的唯一方法。

如要在回應中指定伺服器時間資料,請使用 Server-Timing 回應標頭。範例如下:

HTTP/1.1 200 OK

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

然後在頁面上透過 Resource Timing 和 Navigation Timing API 讀取 resourcenavigation 項目的資料。

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}