評估與下一個顯示的內容的互動 (INP)

1. 簡介

這是互動式程式碼研究室,可讓您瞭解如何使用 web-vitals 程式庫評估與下一個顯示的內容互動 (INP)

必要條件

學習目標

  • 如何將 web-vitals 程式庫新增至網頁,並使用其歸因資料。
  • 使用歸因資料診斷哪些地方可著手改善 INP。

軟硬體需求

  • 一台能夠從 GitHub 複製程式碼及執行 npm 指令的電腦。
  • 文字編輯器。
  • 新版 Chrome 支援所有互動測量功能。

2. 做好準備

取得並執行程式碼

您可以在 web-vitals-codelabs 存放區中找到這個程式碼。

  1. 複製終端機中的存放區:git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
  2. 瀏覽到複製的目錄:cd web-vitals-codelabs/measuring-inp
  3. 安裝依附元件:npm ci
  4. 啟動網路伺服器:npm run start
  5. 透過瀏覽器前往 http://localhost:8080/

試用頁面

本程式碼研究室使用 Gastropodicon (熱門的蝸牛圖解參考網站) 探索 INP 的潛在問題。

Gastropodicon 示範頁面的螢幕截圖

嘗試與網頁互動,讓使用者感受到哪些互動速度較慢。

3. 使用 Chrome 開發人員工具

透過更多工具開啟開發人員工具開發人員工具選單:在頁面上按一下滑鼠右鍵並選取「檢查」,或者使用鍵盤快速鍵

在本程式碼研究室中,我們將同時使用「Performance」面板和「Console」。您隨時可以在開發人員工具頂端的分頁標籤之間切換。

  • INP 問題最常發生在行動裝置上,因此請改用行動裝置多媒體模擬
  • 如果您使用桌上型電腦或筆記型電腦進行測試,效能可能會遠高於實體行動裝置。如要查看更實際的效能檢查結果,請點選「Performance」面板右上角的齒輪,然後選取「CPU 4 倍減速」

開發人員工具效能面板的螢幕截圖,位於應用程式旁邊,已選取 4 倍 CPU 減速

4. 安裝中 網頁 Vitals

web-vitals 是 JavaScript 程式庫,可用於評估使用者體驗的 Web Vitals 指標。您可以利用這個程式庫擷取這些值,並引導他們至數據分析端點,以便之後進行分析,進而找出互動速度緩慢的時間和位置。

將程式庫新增至頁面的方法有很多種,您將如何在自己的網站上安裝程式庫,取決於您管理依附元件的方式、建構程序和其他因素。請務必查看程式庫的說明文件,瞭解所有可用選項。

本程式碼研究室會從 npm 安裝並直接載入指令碼,避免深入瞭解特定建構程序。

您可以使用兩種版本的 web-vitals

  • 「標準」如要追蹤網頁載入中 Core Web Vitals 的指標值,請使用 build。
  • 「歸因」版本會在每個指標中加入額外的偵錯資訊,以便診斷指標得出其本身值的原因。

為了在本程式碼研究室中評估 INP,我們需要歸因版本。

執行 npm install -D web-vitals,將 web-vitals 新增至專案的 devDependencies

在頁面中加入 web-vitals

將指令碼的歸因版本新增至 index.html 底部,並將結果記錄至控制台:

<script type="module">
  import {onINP} from './node_modules/web-vitals/dist/web-vitals.attribution.js';

  onINP(console.log);
</script>

立即試用

嘗試開啟控制台,再次與頁面互動。當您在頁面隨意點選時,系統不會記錄任何資訊!

系統會在網頁的整個生命週期中評估 INP,因此根據預設,web-vitals 會在使用者離開或關閉網頁後回報 INP。這是分析類似指標的理想行為,但不太適合以互動方式偵錯。

web-vitals 提供 reportAllChanges 選項,以提供更詳細的報表。啟用後,系統不會回報每次互動,但只要互動速度比先前任何互動慢,系統就會回報該互動。

請嘗試在指令碼中加入選項,然後再次與網頁互動:

<script type="module">
  import {onINP} from './node_modules/web-vitals/dist/web-vitals.attribution.js';

  onINP(console.log, {reportAllChanges: true});
</script>

重新整理網頁和互動資訊現在應該就會回報給控制台,只要有新的緩慢回應即會更新。例如,請嘗試在搜尋框中輸入字詞,然後刪除輸入框。

開發人員工具控制台的螢幕截圖 (顯示成功輸出 INP 訊息)

5. 歸因是什麼?

我們先從大多數使用者與網頁互動的最初互動開始,Cookie 同意對話方塊。

許多網頁都有需要同步觸發 Cookie 的指令碼,在使用者接受 Cookie 時同步觸發,因此會造成點擊的互動速度變慢。就是這樣

按一下「Yes」接受 (試用版) Cookie,然後查看開發人員工具控制台中記錄的 INP 資料。

已登入開發人員工具控制台的 INP 資料物件

網頁 Vitals 版本和歸因網頁都同時提供這項頂層資訊:

{
  name: 'INP',
  value: 344,
  rating: 'needs-improvement',
  entries: [...],
  id: 'v4-1715732159298-8028729544485',
  navigationType: 'reload',
  attribution: {...},
}

從使用者點選下一個畫作算起的時間長度為 344 毫秒,表示「需要改善」INPentries 陣列包含所有與這次互動相關聯的 PerformanceEntry 值,在本例中為只有一個點擊事件。

如要瞭解這段期間內的情況,我們對 attribution 屬性最感興趣。為了建立歸因資料,web-vitals 會找出與點擊事件重疊的「長動畫影格 (LoAF)」。接著,LoAF 就能提供該影格內使用時間的詳細資料,包括執行的指令碼、在 requestAnimationFrame 回呼、樣式和版面配置中花費的時間。

展開 attribution 屬性即可查看更多資訊。資料更加豐富。

attribution: {
  interactionTargetElement: Element,
  interactionTarget: '#confirm',
  interactionType: 'pointer',

  inputDelay: 27,
  processingDuration: 295.6,
  presentationDelay: 21.4,

  processedEventEntries: [...],
  longAnimationFrameEntries: [...],
}

首先,我們會說明當時的互動情形:

  • interactionTargetElement:這是互動元素的即時參照 (如果元素尚未從 DOM 中移除)。
  • interactionTarget:用於在網頁中尋找元素的選取器。

接著,時間會大範圍細分:

  • inputDelay:從使用者開始互動 (例如點按滑鼠) 到該互動事件監聽器開始運作的時間。在本例中,即使開啟 CPU 節流功能,輸入延遲時間也大約只有 27 毫秒。
  • processingDuration:事件監聽器執行完畢所需的時間。通常,網頁的單一事件會有多個事件監聽器 (例如 pointerdownpointerupclick)。如果所有動畫都在同一個動畫影格中執行,系統會將這些動畫合併至這次的值。在這種情況下,處理時間需要 295.6 毫秒,是 INP 時間的大宗。
  • presentationDelay:從事件監聽器完成到瀏覽器完成下一個影格繪製所需的時間。在本例中為 21.4 毫秒。

在診斷需要最佳化的項目時,這些 INP 階段是重要的信號。最佳化 INP 指南提供更多關於這個主題的資訊。

再進一步,processedEventEntries 包含五個事件,而不是頂層 INP entries 陣列中的單一事件。其中有何區別?

processedEventEntries: [
  {
    name: 'mouseover',
    entryType: 'event',
    startTime: 1801.6,
    duration: 344,
    processingStart: 1825.3,
    processingEnd: 1825.3,
    cancelable: true
  },
  {
    name: 'mousedown',
    entryType: 'event',
    startTime: 1801.6,
    duration: 344,
    processingStart: 1825.3,
    processingEnd: 1825.3,
    cancelable: true
  },
  {name: 'mousedown', ...},
  {name: 'mouseup', ...},
  {name: 'click', ...},
],

頂層項目是 INP 事件,在本例中為點擊。歸因 processedEventEntries 是指在同一個影格中處理的所有事件。請注意,其中包含 mouseovermousedown 等其他事件,而不只是點擊事件。如果其他事件也速度過慢,瞭解這些事件也是一大關鍵,因為這些事件都會導致回應速度變慢。

最後是 longAnimationFrameEntries 陣列。這可能只是一個項目,但在某些情況下,互動可能分散在多個頁框中。這是最簡單的情況,採用單一長動畫影格。

longAnimationFrameEntries

展開 LoAF 項目:

longAnimationFrameEntries: [{
  name: 'long-animation-frame',
  startTime: 1823,
  duration: 319,

  renderStart: 2139.5,
  styleAndLayoutStart: 2139.7,
  firstUIEventTimestamp: 1801.6,
  blockingDuration: 268,

  scripts: [{...}]
}],

這裡有許多實用的值,例如細分樣式所花費的時間。如要進一步瞭解這些屬性,請參閱長動畫影格 API 一文。目前我們主要關注的是 scripts 屬性,這個屬性內含負責長時間執行影格的指令碼詳細資料:

scripts: [{
  name: 'script',
  invoker: 'BUTTON#confirm.onclick',
  invokerType: 'event-listener',

  startTime: 1828.6,
  executionStart: 1828.6,
  duration: 294,

  sourceURL: 'http://localhost:8080/third-party/cmp.js',
  sourceFunctionName: '',
  sourceCharPosition: 1144
}]

在這個範例中,我們可以分辨時間主要是在單一 event-listener 中,於 BUTTON#confirm.onclick 叫用。我們甚至可以看到指令碼來源網址和定義函式的字元位置!

重點摘要

這個歸因資料如何判斷這種狀況?

  • 互動是由按下 button#confirm 元素 (來自指令碼歸因項目的 attribution.interactionTargetinvoker 屬性) 時觸發。
  • 時間主要花在執行事件監聽器 (從 attribution.processingDuration 與總指標 value 相比)。
  • 緩慢事件監聽器程式碼是從 third-party/cmp.js 中定義的點擊事件監聽器開始 (來自 scripts.sourceURL)。

這些資料足以讓我們知道哪些地方需要最佳化!

6. 多個事件監聽器

請重新整理頁面,讓開發人員工具控制台清楚顯示,且 Cookie 同意聲明互動不再是最長的互動時間。

在搜尋框中輸入搜尋字詞。歸因資料會顯示哪些資訊?你覺得為什麼會這樣?

歸因分析資料

首先,以一個範例測試範例的大規模掃描:

{
  name: 'INP',
  value: 1072,
  rating: 'poor',
  attribution: {
    interactionTargetElement: Element,
    interactionTarget: '#search-terms',
    interactionType: 'keyboard',

    inputDelay: 3.3,
    processingDuration: 1060.6,
    presentationDelay: 8.1,

    processedEventEntries: [...],
    longAnimationFrameEntries: [...],
  }
}

這是透過鍵盤與 input#search-terms 元素互動的 INP 值 (已啟用 CPU 節流功能)。大多數時間 (總 INP 總和 1072 毫秒為 1061 毫秒) 都花在處理處理時間。

不過,scripts 項目比較有趣。

版面配置輾轉

第一個 scripts 陣列項目提供了寶貴的背景資訊:

scripts: [{
  name: 'script',
  invoker: 'BUTTON#confirm.onclick',
  invokerType: 'event-listener',

  startTime: 4875.6,
  executionStart: 4875.6,
  duration: 497,
  forcedStyleAndLayoutDuration: 388,

  sourceURL: 'http://localhost:8080/js/index.js',
  sourceFunctionName: 'handleSearch',
  sourceCharPosition: 940
},
...]

大多數的處理時間長度會在這個指令碼執行期間發生,也就是 input 事件監聽器 (叫用端為 INPUT#search-terms.oninput)。函式名稱 (handleSearch) 和 index.js 來源檔案中的字元位置一樣。

主要是新屬性:forcedStyleAndLayoutDuration。這是在這個指令碼叫用中,被強制瀏覽器重新安排網頁配置的時間。換句話說,在 497 毫秒裡,有 78% 的時間 (用來執行這個事件監聽器的時間為 388 毫秒),實際上是浪費版面配置。

這是解決問題的首要之務。

重複事件監聽器

從個別觀點來看,接下來兩個指令碼項目並沒有特別出色的優點:

scripts: [...,
{
  name: 'script',
  invoker: '#document.onkeyup',
  invokerType: 'event-listener',

  startTime: 5375.3,
  executionStart: 5375.3,
  duration: 124,

  sourceURL: 'http://localhost:8080/js/index.js',
  sourceFunctionName: '',
  sourceCharPosition: 1526,
},
{
  name: 'script',
  invoker: '#document.onkeyup',
  invokerType: 'event-listener',

  startTime: 5673.9,
  executionStart: 5673.9,
  duration: 95,

  sourceURL: 'http://localhost:8080/js/index.js',
  sourceFunctionName: '',
  sourceCharPosition: 1526
}]

兩個項目都是 keyup 事件監聽器,請依序執行一個事件監聽器。事件監聽器是匿名函式 (因此 sourceFunctionName 屬性不會回報任何資訊),但仍有來源檔案和字元位置,以便我們找出程式碼的位置。

請注意,兩者的來源檔案和字元位置皆相同

瀏覽器在單一動畫影格中處理多次按鍵,導致此事件監聽器執行了兩次,之後才能繪製任何物件!

此外,當事件監聽器完成的時間越長,這類效果也會變得更加複雜,因此會加入越多輸入事件,延長緩慢的互動。

由於這是搜尋/自動完成互動,因此捨棄輸入內容會是不錯的策略,這樣最多在每個影格最多只會處理一次按鍵動作。

7. 輸入延遲

主要執行緒忙碌中,會造成輸入延遲的常見原因 (從使用者互動到事件監聽器可以開始處理互動的時間)。可能的原因有很多:

  • 系統正在載入網頁,主執行緒正忙於設定 DOM、設定頁面及設定頁面樣式,以及評估和執行指令碼。
  • 該網頁通常處於忙碌狀態,例如執行運算、指令碼式動畫或廣告。
  • 先前的互動需要很長的時間處理,因此會延遲上一個範例中出現的未來互動。

示範頁面有秘密功能,只要按一下頁面頂端的圖片標誌,就會開始動畫,並執行一些複雜的主執行緒 JavaScript 作業。

  • 按一下蝸牛標誌可開始播放動畫。
  • 蝸牛位於跳出底部時,會觸發 JavaScript 工作。請試著與網頁互動,最好盡可能靠近退信底部,查看可觸發的 INP 高度。

舉例來說,即使您沒有觸發其他事件監聽器 (例如在蝸牛彈跳時,按下搜尋框並將搜尋框焦點直接聚焦),主執行緒作業會導致網頁在一段時間內沒有回應。

對許多頁面而言,繁重的主執行緒工作並無法負荷主要執行緒,但我們可以模擬在 INP 歸因資料中如何識別這些執行緒。

以下的歸屬範例說明在蝸牛彈跳時只聚焦於搜尋框:

{
  name: 'INP',
  value: 728,
  rating: 'poor',

  attribution: {
    interactionTargetElement: Element,
    interactionTarget: '#search-terms',
    interactionType: 'pointer',

    inputDelay: 702.3,
    processingDuration: 4.9,
    presentationDelay: 20.8,

    longAnimationFrameEntries: [{
      name: 'long-animation-frame',
      startTime: 2064.8,
      duration: 790,

      renderStart: 2065,
      styleAndLayoutStart: 2854.2,
      firstUIEventTimestamp: 0,
      blockingDuration: 740,

      scripts: [{...}]
    }]
  }
}

如預測,事件監聽器會快速執行 (顯示處理時間為 4.9 毫秒,而絕大多數的互動都花在輸入延遲,佔總 728 的 702.3 毫秒。

這種情況較難以偵錯。雖然我們瞭解使用者的互動方式和互動方式,但我們知道一部分的互動很快就完成了,並未造成任何問題。結果是網頁上其他會延遲互動開始處理的因素,但我們該如何得知該從何處著手?

LoAF 指令碼項目的作用是拯救這一天:

scripts: [{
  name: 'script',
  invoker: 'SPAN.onanimationiteration',
  invokerType: 'event-listener',

  startTime: 2065,
  executionStart: 2065,
  duration: 788,

  sourceURL: 'http://localhost:8080/js/index.js',
  sourceFunctionName: 'cryptodaphneCoinHandler',
  sourceCharPosition: 1831
}]

雖然這個函式與互動無關,但會放慢動畫影格的速度,因此包含在與互動事件彙整的 LoAF 資料中。

透過此程式碼,我們可以瞭解延遲互動處理的函式如何觸發 (由 animationiteration 事件監聽器)、確切的函式負責,以及該函式在來源檔案中的位置。

8. 簡報延遲:當有更新無法顯示時

呈現方式延遲是指事件監聽器配合事件執行完畢後,直到瀏覽器可以在畫面上繪製新影格為止,並顯示使用者可見的意見回饋。

重新整理頁面並再次重設 INP 值,然後開啟漢堡選單。它開啟時一定大吃一驚。

它會是什麼樣子?

{
  name: 'INP',
  value: 376,
  rating: 'needs-improvement',
  delta: 352,

  attribution: {
    interactionTarget: '#sidenav-button>svg',
    interactionType: 'pointer',

    inputDelay: 12.8,
    processingDuration: 14.7,
    presentationDelay: 348.5,

    longAnimationFrameEntries: [{
      name: 'long-animation-frame',
      startTime: 651,
      duration: 365,

      renderStart: 673.2,
      styleAndLayoutStart: 1004.3,
      firstUIEventTimestamp: 138.6,
      blockingDuration: 315,

      scripts: [{...}]
    }]
  }
}

這次是顯示延遲,佔了大多數緩慢互動的成因。這表示在事件監聽器完成之後,會造成主執行緒遭到阻斷。

scripts: [{
  entryType: 'script',
  invoker: 'FrameRequestCallback',
  invokerType: 'user-callback',

  startTime: 673.8,
  executionStart: 673.8,
  duration: 330,

  sourceURL: 'http://localhost:8080/js/side-nav.js',
  sourceFunctionName: '',
  sourceCharPosition: 1193,
}]

查看 scripts 陣列中的單一項目時,可以看到 FrameRequestCallbackuser-callback 中所花費的時間。這次顯示延遲是由 requestAnimationFrame 回呼造成。

9. 結語

匯總欄位資料

值得注意的是,在從單次載入網頁中的單一 INP 歸因項目查看操作會更加容易。如何匯總這項資料,以便根據欄位資料對 INP 進行偵錯?如果提供實用的細節,實際上就會比較困難。

舉例來說,如果知道哪個網頁元素是互動速度緩慢的常見來源,會很有幫助。不過,如果網頁經過編譯的 CSS 類別名稱從建構變更為建構,相同元素的 web-vitals 選取器在不同版本中可能會有所不同。

相反地,您必須思考特定應用程式,決定何者最為實用,以及如何匯總資料。舉例來說,在向歸因資料指出歸因資料前,您可以根據目標所在的元件或目標執行的 ARIA 角色,將 web-vitals 選取器換成自己的 ID。

同樣地,scripts 項目的 sourceURL 路徑可能含有檔案型雜湊,這使得程式碼難以合併,但您可以先根據已知的建構程序移除雜湊,再向資料交還。

然而,沒有這種複雜的資料沒有簡單的路徑,但就算使用一部分的資料,比起完全沒有歸因資料,對偵錯程序來說還是有價值。

各地歸因!

以 LoAF 為基礎的 INP 歸因是一項強大的偵錯輔助功能。針對 INP 期間的具體情況,提供精細的資料。在多數情況下,這項功能可讓您指出指令碼中要開始最佳化作業的位置。

現在可以在任何網站上使用 INP 歸因資料了!

即使您無權編輯頁面,也可以在開發人員工具控制台中執行以下程式碼片段,藉此重新建立程序:

const script = document.createElement('script');
script.src = 'https://unpkg.com/web-vitals@4/dist/web-vitals.attribution.iife.js';
script.onload = function () {
  webVitals.onINP(console.log, {reportAllChanges: true});
};
document.head.appendChild(script);

瞭解詳情