您可以透過以使用者為中心的指標,在任何網站上普遍評估使用者體驗,這類指標的價值相當高。這些指標可協助您:
- 瞭解實際使用者對整個網路的體驗。
- 與競爭對手的網站比較。
- 在數據分析工具中追蹤實用且可做為行動依據的資料,不必撰寫自訂程式碼。
通用指標是不錯的基準,但在許多情況下,您需要評估更多指標,才能全面掌握特定網站的使用體驗。
自訂指標可評估網站體驗的各個面向,這些面向可能只適用於您的網站,例如:
- 單頁應用程式 (SPA) 從一個「頁面」轉換到另一個「頁面」所需的時間。
- 網頁顯示從資料庫擷取資料的時間長度 (適用於已登入使用者)。
- 伺服器端算繪 (SSR) 應用程式水合所需時間。
- 回訪者載入資源的快取命中率。
- 遊戲中點擊或鍵盤事件的延遲時間。
用於評估自訂指標的 API
過去,網頁開發人員可用的低階 API 不多,因此必須採用變通做法,才能評估網站成效。
舉例來說,您可以執行 requestAnimationFrame 迴圈並計算每個影格之間的差異,判斷主執行緒是否因長時間執行的 JavaScript 工作而遭到封鎖。如果差異明顯大於螢幕的影格速率,您可以將其回報為長時間工作。不過,我們不建議使用這類方法,因為這些方法本身會影響效能 (例如耗盡電池電量)。
有效評估成效的第一條原則,就是確保成效評估技術本身不會造成成效問題。因此,如要評估網站上的任何自訂指標,請盡量使用下列任一 API。
Performance Observer API
Performance Observer API 是一種機制,可收集及顯示本頁討論的所有其他 Performance 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 (例如 largest-contentful-paint) 不會透過 performance 物件公開,只會透過 PerformanceObserver 公開。
除非您特別需要與 Internet Explorer 相容,否則最好避免在程式碼中使用這些方法,並改用 PerformanceObserver。
User Timing 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 開發人員工具會在「效能」面板中顯示使用者時間測量結果,許多分析服務供應商也會自動追蹤您進行的任何測量,並將時間長度資料傳送至分析後端。
如要回報 User Timing 測量結果,可以使用 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
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
Long Animation Frames API 是 Long Tasks API 的新版本,會查看超過 50 毫秒的長影格,而非長工作。這項 API 可解決 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});
Element Timing API
最大內容繪製 (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>
Event Timing API
Interaction to Next Paint (INP) 指標會觀察網頁生命週期內的所有點擊、輕觸和鍵盤互動,評估網頁的整體回應速度。網頁的 INP 通常是完成時間最長的互動,從使用者發起互動到瀏覽器繪製下一個影格,顯示使用者輸入內容的視覺結果,這段時間就是 INP。
事件時間軸 API 可提供 INP 指標。這個 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
開發人員可透過 Resource Timing API 深入瞭解特定網頁的資源載入方式。雖然 API 名稱是「時間」,但提供的資訊不只包含時間資料 (不過這類資料也很多)。您可存取的其他資料包括:
initiatorType:資源的擷取方式,例如來自<script>或<link>代碼,或是來自fetch()呼叫。nextHopProtocol:用於擷取資源的通訊協定,例如h2或quic。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});
Navigation Timing API
Navigation Timing API 與 Resource Timing API 類似,但只會回報導覽要求。navigation 項目類型與 resource 項目類型類似,但包含僅適用於導覽要求的額外資訊 (例如 DOMContentLoaded 和 load 事件觸發時)。
許多開發人員會追蹤伺服器回應時間 (第一個位元組時間 (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 啟動時間。這是指瀏覽器啟動 Service Worker 執行緒所需的時間長度,之後才能開始攔截擷取事件。
特定導覽要求的 Service Worker 啟動時間,可從 entry.responseStart 和 entry.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
伺服器時間 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 的 resource 或 navigation 項目資料。
// 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});