尋找欄位中的緩慢互動

瞭解如何從網站欄位資料中找出緩慢互動,瞭解如何提升與下一個顯示的內容互動。

實際資料:這類資料可讓您瞭解使用者實際造訪網站的情形,並闡明單憑研究室資料無法找到的問題。在擔心「與下一個顯示的內容互動 (INP)」問題時,現場資料是找出緩慢互動的關鍵,並提供重要線索來協助您修正。

本指南將說明如何使用 Chrome 使用者體驗報告 (CrUX) 中的欄位資料,快速評估網站的 INP,查看網站是否有 INP 問題。接下來,您將瞭解如何使用 Web-vitals JavaScript 程式庫的歸因版本,以及 Long Animation Frames API (LoAF) 所提供的新深入分析,來收集和解讀網站上緩慢互動的欄位資料。

從 CrUX 開始評估網站的 INP

如果你無法收集網站使用者的欄位資料,建議先從 CrUX 開始著手。針對選擇傳送遙測資料的 Chrome 使用者,CrUX 會收集實際資料。

CrUX 資料會出現在多個不同區域中,具體取決於您所尋找的資訊範圍。CrUX 可提供 INP 和其他 Core Web Vitals 資料,協助您:

  • 使用 PageSpeed Insights 建立個別網頁和整個來源。
  • 網頁類型。舉例來說,許多電子商務網站都有「產品詳細資料頁面」和「產品資訊網頁」類型。你可以在 Search Console 中取得不重複網頁類型的 CrUX 資料。

首先,您可以在 PageSpeed Insights 中輸入網站網址。輸入網址後,系統就會顯示多個指標 (包括 INP) 的欄位資料 (如果有的話)。您也可以使用切換按鈕查看行動裝置和電腦維度的 INP 值。

PageSpeed Insights 中由 CrUX 所顯示的欄位資料,提供三個網站體驗核心指標的 LCP、INP、CLS,以及 TTFB、FCP 診斷指標,以及已淘汰的 Core Web Vitals 指標。
PageSpeed Insights 中所顯示的 CrUX 資料讀數。在本例中,指定網頁的 INP 需要改善。

這項資料非常實用,因為你可以藉此得知問題所在。不過,CrUX 功能無法運作,可讓您說明造成問題的原因。許多實際使用者監控 (RUM) 解決方案可協助您從網站使用者收集自己的欄位資料來找出這個答案,而其中一種做法是自行透過 Web Vitals JavaScript 程式庫收集該現場資料。

使用 web-vitals JavaScript 程式庫收集欄位資料

您可以在網站上載入 web-vitals JavaScript 程式庫的指令碼,藉此收集網站使用者的欄位資料。您可以使用這個控制項記錄多項指標,包括支援這項功能的瀏覽器中 INP。

瀏覽器支援

  • 96
  • 96
  • x
  • x

來源

Web Vitals 程式庫的標準版本可用來取得現場使用者的基本 INP 資料:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  console.log(name);    // 'INP'
  console.log(value);   // 512
  console.log(rating);  // 'poor'
});

為了分析使用者的欄位資料,建議您將這些資料傳送至下列位置:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  // Prepare JSON to be sent for collection. Note that
  // you can add anything else you'd want to collect here:
  const body = JSON.stringify({name, value, rating});

  // Use `sendBeacon` to send data to an analytics endpoint.
  // For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
  navigator.sendBeacon('/analytics', body);
});

然而,這些資料本身並未比 CrUX 還要多。此時,Web-vitals 程式庫的歸因資料就能派上用場。

進一步運用 Web-Vitals 程式庫的歸因版本

網頁 Vitals 資料庫的歸因版本會顯示可在實際環境中取得的其他資料,協助您進一步解決影響網站 INP 的有問題互動。您可以透過程式庫的 onINP() 方法中顯示的 attribution 物件存取這項資料:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, rating, attribution}) => {
  console.log(name);         // 'INP'
  console.log(value);        // 512
  console.log(rating);       // 'poor'
  console.dir(attribution);  // Attribution data
});
Web-Vitals 程式庫的主控台記錄顯示方式這個範例中的主控台顯示指標名稱 (INP)、INP 值 (56)、該值位於 INP 門檻 (良好) 內,以及歸因物件中顯示的各種資訊位元 (包括 Long Animation Frame API 的項目)。
Web-Vitals 資料庫中的資料如何在控制台中顯示。

除了網頁的 INP 本身,歸因建構也可以提供許多資料,有助您瞭解互動速度緩慢的原因,包括您應著重的互動中的哪些部分。這項功能可協助您解答重要問題,例如:

  • 「使用者在載入網頁時是否與網頁互動?」
  • 「互動的事件處理常式是否長時間執行?」
  • 「互動事件處理常式程式碼是否延遲啟動?如果是,當時主執行緒上還有哪些事情?」
  • 「互動是否造成大量轉譯工作導致下一個影格延遲繪製?」

下表列出您可以從程式庫中取得的部分基本歸因資料,協助您瞭解網站互動速度緩慢的部分主要原因:

attribution 物件金鑰 資料
interactionTarget CSS 選取器,指向產生頁面 INP 值的元素,例如 button#save
interactionType 互動的類型,可能是點擊、輕觸或鍵盤輸入。
inputDelay* 互動的輸入延遲
processingDuration* 從第一個事件監聽器開始執行的時間,以回應使用者互動,到所有事件監聽器處理程序都結束為止。
presentationDelay* 互動的「呈現延遲」,從事件處理常式完成到繪製下一個影格的時間開始。
longAnimationFrameEntries* 與互動相關的 LoAF 項目。如需更多資訊,請參閱下一節。
*第 4 版的新功能

從 Web-Vitals 程式庫第 4 版開始,您可以運用 INP 階段細目 (輸入延遲、處理時間長度和顯示延遲時間) 和 Long Animation Frame API (LoAF),進一步深入瞭解有問題的互動。

長動畫頁框 API (LoAF)

瀏覽器支援

  • 123
  • 123
  • x
  • x

來源

使用欄位資料對互動進行偵錯是一項艱鉅的工作。但有了 LoAF 的資料,你就可以進一步瞭解互動速度緩慢的原因,因為 LoAF 提供了多項詳細的時間點和其他資料,有助您找出精確的原因 (更重要的是,在網站程式碼中,發生問題的來源)。

Web-Vitals 程式庫的歸因建構會在 attribution 物件的 longAnimationFrameEntries 鍵下方顯示 LoAF 項目的陣列。下表列出每個 LoAF 項目中可供查看的幾個關鍵資料:

LoAF 項目物件金鑰 資料
duration 動畫影格的時間長度,到版面配置完成時為止,但不含繪製和合成。
blockingDuration 影格中瀏覽器由於長時間工作而無法快速回應的總時間。這段封鎖時間可能包括執行 JavaScript 的長時間工作,以及影格中所有後續的長時間轉譯工作。
firstUIEventTimestamp 事件在影格中排入佇列的時間戳記。適合用來判斷互動的輸入延遲開始。
startTime 影格的開始時間戳記。
renderStart 影格轉譯工作開始的時間。這包括所有 requestAnimationFrame 回呼 (以及 ResizeObserver 回呼 (如適用),但可能會在任何樣式/版面配置工作開始前進行)。
styleAndLayoutStart 在頁框中套用樣式/版面配置時。這個函式在辨識其他可用時間戳記時,可用於判斷樣式/版面配置的工作長度。
scripts 一系列項目,內含會影響網頁 INP 的指令碼作者資訊。
以 LoAF 模型為基準的長動畫影格視覺化。
根據 LoAF API (減號 blockingDuration) 的長動畫影格時間圖表。

以上資訊都可以讓您充分瞭解互動速度緩慢的原因,但 LoAF 項目顯示的 scripts 陣列應特別注意:

指令碼歸因物件索引鍵 資料
invoker 叫用者。可否依下一列說明的叫用者類型而有所不同。叫用者範例可以是 'IMG#id.onload''Window.requestAnimationFrame''Response.json.then' 等值。
invokerType 叫用者的類型。可以是 'user-callback''event-listener''resolve-promise''reject-promise''classic-script''module-script'
sourceURL 產生長動畫頁框的指令碼網址。
sourceCharPosition sourceURL 識別的指令碼中的字元位置。
sourceFunctionName 在已找到的指令碼中,函式的名稱。

這個陣列中的每個項目包含這個表格中顯示的資料,針對導致互動速度緩慢的指令碼及問題負責的方式提供相關資訊。

評估並找出互動速度緩慢導致的常見原因

為了讓您瞭解這些資訊的運用方式,本指南現在將逐步說明如何使用 web-vitals 程式庫中的 LoAF 資料,找出互動速度緩慢導致的一些原因。

處理時間過長

互動的處理時間是指互動已註冊的事件處理常式回呼要執行完畢,以及間隔期間發生任何其他事件所需的時間。Web Vitals 程式庫會顯示較長的處理時間:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5
});

事件處理常式程式碼的執行時間過長,這是正常現象,有時這是正常現象!確認問題所在後,您就能運用 LoAF 資料瞭解詳情:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5

  // Get the longest script from LoAF covering `processingDuration`:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Get attribution for the long-running event handler:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

如上述程式碼片段所示,您可以使用 LoAF 資料,追蹤處理時間長度偏高的互動背後的確切原因,包括:

  • 元素及其已註冊的事件監聽器。
  • 指令碼檔案及其中的字元位置,包含長時間執行的事件處理常式程式碼。
  • 函式的名稱。

這類資料十分珍貴。您不必再費心找出哪項互動 (或其哪個事件處理常式) 負擔了高處理時間長度值。此外,由於第三方指令碼通常可註冊自己的事件處理常式,因此您可以判斷程式碼是否由他們負責!針對您可控制的程式碼,請參閱最佳化長時間工作

長時間輸入延遲

長時間執行的事件處理常式很常見,但互動的其他部分卻要考量。其中一個階段發生在處理持續時間之前,也就是「輸入延遲」。也就是從使用者啟動互動開始,到事件處理常式回呼開始執行的時間,以及主執行緒已處理另一項工作時。Web Vitals 程式庫的歸因版本可顯示互動的輸入延遲時間:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536
});

如果您發現某些互動的輸入延遲時間很長,就必須瞭解導致輸入延遲過長的互動發生時在網頁上發生的狀況,這經常與網頁是網頁載入還是之後發生。

是否在載入網頁時出現?

當網頁載入時,主執行緒經常會變得很忙碌。在這段時間內,所有類型的工作都排入佇列並進行處理,如果使用者在所有作業進行期間嘗試與網頁互動,可能會延遲互動。載入大量 JavaScript 的網頁可以開始編譯和評估指令碼,並執行函式,讓網頁準備好進行使用者互動。如果發生此活動時使用者實際發生了互動,這個方法可能會產生阻礙,而您可以瞭解網站使用者是否有此情況:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Invoker types can describe if script eval blocked the main thread:
    const {invokerType} = script;    // 'classic-script' | 'module-script'
    const {sourceLocation} = script; // 'https://example.com/app.js'
  }
});

如果您在欄位中記錄這類資料,並看到 'classic-script''module-script' 的輸入延遲和叫用類型偏高,可以確定網站上的指令碼需要較長的評估時間,而且封鎖的主執行緒有足夠時間會延遲互動。如要縮短這類封鎖時間,請將指令碼分割成較小的套件,延遲稍後載入初始未使用的程式碼,並檢查網站上是否有未使用的程式碼。您可以完全移除。

是否在網頁載入之後?

雖然輸入延遲經常在網頁載入期間發生,但也可能因為完全不同的原因,在網頁載入「之後」才發生輸入延遲的情形。導致輸入延遲的常見原因可能是程式碼因先前的 setInterval 呼叫而定期執行,甚至是稍早排入佇列且仍在處理的事件回呼。

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    const {invokerType} = script;        // 'user-callback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

就像先前解決的處理持續時間值偏高的情況一樣,由於先前所述原因而輸入延遲時間過長,系統會提供詳細的指令碼歸因資料。但不同之處在於,叫用器類型會根據延遲互動的工作性質而改變:

  • 'user-callback' 表示封鎖工作來自 setIntervalsetTimeout,甚至是 requestAnimationFrame
  • 'event-listener' 表示封鎖工作來自先前排入佇列且仍在處理的輸入。
  • 'resolve-promise''reject-promise' 表示封鎖工作來自先前啟動的一些非同步工作,而且會在使用者嘗試與網頁互動時延遲解決或遭拒,導致互動延遲。

無論如何,您都能透過指令碼歸因資料瞭解該從何處著手,以及輸入延遲是因自己的程式碼或第三方指令碼所致。

簡報長時間延遲

顯示延遲是指互動事件的最後一里,而且是從互動的事件處理常式完成時開始,一直到繪製下一個影格的時間點為止。當事件處理常式因互動而變更使用者介面的視覺狀態時,就會發生這類錯誤。就像處理時間長度和輸入延遲一樣,Web-Vitals 程式庫可告訴您互動的簡報延遲多久時間:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691
});

如果記錄這些資料,卻發現網站 INP 發生互動發生延遲現象,就會有嚴重的延遲情形,以下列出幾個需要留意的原因。

昂貴的風格和版面配置工作

長時間顯示簡報延遲可能會導致樣式重新計算版面配置工作因多種原因而增加,包括複雜的 CSS 選取器和大型 DOM 大小。您可以使用 Web Vitals 程式庫中顯示的 LoAF 時間,評估這項作業的持續時間:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get necessary timings:
  const {startTime} = loaf; // 2120.5
  const {duration} = loaf;  // 1002

  // Figure out the ending timestamp of the frame (approximate):
  const endTime = startTime + duration; // 3122.5

  // Get the start timestamp of the frame's style/layout work:
  const {styleAndLayoutStart} = loaf; // 3011.17692309

  // Calculate the total style/layout duration:
  const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running style and layout operation:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

LoAF 不會指出影格樣式和版面配置作業的時間長度,但會在開始時通知你。有了此開始時間戳記,您就可以使用 LoAF 中的其他資料計算影格的結束時間,然後減去樣式和版面配置的起始時間戳記,以計算出精確的工作時間長度。

長時間執行的 requestAnimationFrame 回呼

造成簡報延遲過長的原因可能是 requestAnimationFrame 回呼中執行過多工作。此回呼的內容會在事件處理常式執行完畢之後執行,但會在樣式重新計算和版面配置工作之前執行。

如果回呼作業相當複雜,這些回呼可能需要耗費大量時間才能完成。如果您認為簡報延遲值是因為 requestAnimationFrame 正在執行工作而造成,可以使用 Web Vitals 程式庫顯示的 LoAF 資料來找出以下情況:

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 543.1999999880791

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get the render start time and when style and layout began:
  const {renderStart} = loaf;         // 2489
  const {styleAndLayoutStart} = loaf; // 2989.5999999940395

  // Calculate the `requestAnimationFrame` callback's duration:
  const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running requestAnimationFrame callback:
    const {invokerType} = script;        // 'user-callback'
    const {invoker} = script;            // 'FrameRequestCallback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

如果您發現 requestAnimationFrame 回呼中有絕大部分的顯示延遲時間,請確認您在這些回呼中執行的工作僅限於執行會導致使用者介面實際更新的工作。其他未觸及 DOM 或更新樣式的工作,都會導致繪製下一個影格的延遲,因此請小心謹慎!

結論

現場資料是瞭解實際使用者有哪些互動問題的最佳來源。只要採用網站 Vitals JavaScript 程式庫 (或 RUM 供應商) 等現場資料收集工具,您就能更放心瞭解哪些互動最有問題,然後繼續在研究室中重現有問題的互動行為,然後修正問題。

主頁橫幅由 Federico Respini 製作,取自 Unsplash