找出 INP 不佳的原因

如果互動回應不佳,使用者會覺得網頁速度緩慢,甚至完全無法運作。Interaction to Next Paint (INP) 是以 Event Timing API 為基礎的指標。這項指標會評估最差的互動 (不計部分離群值),做為網頁回應速度的衡量標準。

找出網頁沒有回應的根本原因可能很棘手,尤其是因為這取決於使用者互動和環境條件,例如裝置功能和網路條件。相較於載入網頁等可重複且可預測的情境,這類問題更難診斷。實驗室資料只會複製一小部分實際使用者情境,因此必須評估實際環境中 INP 緩慢的原因。

Event Timing API 可將 INP 時間長度分為三個子部分:輸入延遲 (互動發生時已在進行的處理作業)、處理時間長度 (互動直接造成的延遲),以及顯示延遲 (互動後轉譯下一個影格造成的延遲)。這有助於判斷問題是出在其他程式碼、互動 JavaScript 程式碼,還是瀏覽器處理轉譯更新,而不是直接跳到互動程式碼。

您可以進一步瞭解 JavaScript、樣式重新計算和版面配置工作延遲互動的情形。使用 JS Self-Profiling API 進行完整效能追蹤,是相當耗費資源的解決方案,容易造成效能問題。Long Animation Frames API 是一種輕量 API,可用於實際環境,在 INP 互動期間識別執行速度緩慢的 JavaScript,並區分樣式重新計算和版面配置成本:

  • 指令碼層級強制樣式和版面配置 (forcedStyleDuration 和 forcedLayoutDuration):在輸入延遲或處理時間期間,回報執行中指令碼內的同步樣式重新計算 (例如在 DOM 變異後呼叫 getComputedStyle()) 和強制版面配置/重排 (例如在變異後讀取 getBoundingClientRect() 或 offsetWidth)。
  • 影格層級算繪樣式和版面配置 (styleDuration 和 layoutDuration):回報在影格算繪更新期間,重新計算樣式與更新版面配置所花費的時間,有助於解釋呈現延遲過高的原因。

區分樣式重新計算和版面配置至關重要,因為兩者的根本原因和修正方式不同:樣式持續時間過長,代表 CSS 選取器比對成本高昂,或樣式失效範圍過廣 (可透過簡化選取器或縮減規則範圍來修正);版面配置持續時間過長,則代表幾何圖形計算成本高昂,或同步版面配置頻繁變動 (可透過在寫入前批次處理 DOM 讀取、簡化 DOM 結構或使用 CSS 限制來修正)。

導入方式

從原始 Event Timing API 計算 INP 相當複雜,且有幾項細微差異。建議您使用 RUM 工具或程式庫收集這項資料。

web-vitals 是 Google 的開放原始碼程式庫,可計算 Core Web Vitals (包括 INP),也包含 INP 互動的子部分和長動畫影格資料。

使用 web-vitals 程式庫取得 INP 互動的歸因

web-vitals 程式庫是小型程式庫,用於評估 Core Web Vitals 和其他成效指標。onINP() 函式可用來找出最慢的互動,並包含 INP 子部分、互動期間執行的指令碼,以及使用 Long Animation Frames API 的樣式和版面配置持續時間等資訊。

// Use the attribution build to get Long Animation Frame data
// alongside the INP metric value.
import { onINP } from 'web-vitals/attribution';

onINP((metric) => {
  const { attribution } = metric;
  const longestScript = attribution.longestScript;

  // Sum rendering-phase style and layout durations across all LoAF entries
  // intersecting the INP interaction (primarily explains presentationDelay).
  let renderStyleDuration = 0;
  let renderLayoutDuration = 0;
  for (const entry of attribution.longAnimationFrameEntries) {
    renderStyleDuration += entry.styleDuration ?? 0;
    renderLayoutDuration += entry.layoutDuration ?? 0;
  }

  // Beacon script, style, and layout attribution for the INP interaction,
  // so you can identify the root cause in production.
  navigator.sendBeacon(
    '/analytics',
    JSON.stringify({
      name: 'INP',
      value: metric.value,
      // These fields give the INP subparts:
      inputDelay: attribution.inputDelay,
      presentationDelay: attribution.presentationDelay,
      processingDuration: attribution.processingDuration,
      interactionTarget: attribution.interactionTarget,
      // These fields identify which script function was responsible
      // for the longest processing during the INP interaction.
      invokerType: longestScript?.entry?.invokerType,
      sourceURL: longestScript?.entry?.sourceURL,
      sourceFunctionName: longestScript?.entry?.sourceFunctionName,
      sourceCharPosition: longestScript?.entry?.sourceCharPosition,
      // subpart indicates which phase (input delay, processing, or
      // presentation delay) the longest script overlapped with most.
      subpart: longestScript?.subpart,
      intersectingDuration: longestScript?.intersectingDuration,
      // Synchronous style and layout work forced inside the longest script
      // (with forcedStyleAndLayoutDuration as a combined fallback):
      forcedStyleDuration: longestScript?.entry?.forcedStyleDuration,
      forcedLayoutDuration: longestScript?.entry?.forcedLayoutDuration,
      forcedStyleAndLayoutDuration: longestScript?.entry?.forcedStyleAndLayoutDuration,
      // Rendering-phase style and layout work across the interaction's frames
      // (with totalStyleAndLayoutDuration as a combined fallback):
      renderStyleDuration,
      renderLayoutDuration,
      totalStyleAndLayoutDuration: attribution.totalStyleAndLayoutDuration
    })
  );
});

最佳做法

  • 建議先使用 INP 子部分,判斷延遲是否已執行 JavaScript (輸入延遲)、互動的事件處理常式 JavaScript (處理時間),或後續算繪 (呈現延遲)。
  • 建議嘗試找出造成最大阻礙的 JavaScript,並提供額外詳細資料,特別是輸入延遲和處理時間。
  • 務必區分樣式重新計算 (styleDuration、forcedStyleDuration) 和版面配置 (layoutDuration、forcedLayoutDuration),以便診斷處理時間過長或顯示延遲問題:樣式時間過長表示 CSS 選取器耗費大量資源或無效範圍過廣,而版面配置時間過長則表示 DOM 幾何圖形瓶頸或同步版面配置抖動。
  • 建議使用 Long Animation Frames API 提供這類詳細資料,而非 JS Self-Profiling API 等替代方案,因為後者會產生較高的執行階段負擔。
  • 如果沒有其他 RUM 解決方案,請使用 web-vitals 程式庫。這項工具可識別 INP 互動,並提供互動期間執行的子部分和指令碼相關資訊 (使用 Long Animation Frames API)。
  • 務必將必要資訊傳回數據分析服務,而非僅記錄在本機。

瀏覽器支援和備用策略

Event Timing API 適用於大多數新式瀏覽器,是計算 INP 和 INP 子部分所必需。如果瀏覽器不支援這個 API,就無法評估 INP。

Long Animation Frames API 提供選用的額外詳細資料。您可以放心使用,不必擔心備用方案。在大多數情況下,它找出的效能提升機會也適用於其他瀏覽器。

如果 Baseline 目標支援 長動畫影格 API (long-animation-frames),但不支援獨立的樣式和版面配置時間長度屬性 (PerformanceLongAnimationFrameTiming 上的 styleDuration 和 layoutDuration,或 PerformanceScriptTiming 上的 forcedStyleDuration 和 forcedLayoutDuration),請改用合併的樣式和版面配置時間:

  • 在 PerformanceScriptTiming 上使用 script.forcedStyleAndLayoutDuration,在指令碼中同步強制執行樣式和版面配置工作。
  • 在 web-vitals/attribution 中使用 metric.attribution.totalStyleAndLayoutDuration (或在每個 PerformanceLongAnimationFrameTiming 項目上計算 (entry.paintTime || entry.startTime + entry.duration) - entry.styleAndLayoutStart),以結合樣式和版面配置。