---
# System prepended metadata

title: Interaction to Next Paint (INP)
tags: [seo, web vitals]

---

# Interaction to Next Paint (INP)

> 用於評估網頁回應使用者互動的整體效能，方法是觀察使用者造訪網頁期間，網頁對於所有點擊、輕觸和鍵盤互動的延遲時間
![image](https://hackmd.io/_uploads/BkgdtrqHJl.png)

- 使用者停留在頁面 90% 的時間都會在載入之後，因此確保回應速度變得非常重要，而這也是 INP 在評估的部分
- 好的 Responsiveness 代表頁面能迅速回應使用者的互動；在做出回應時，瀏覽器會提供視覺回饋如『新增購物車的商品是否確實加入』、『手機版選單是否確實打開』、『登入表單是否正在被伺服器驗證』等等
- 有些互動自然會比其他互動耗時更久，但對於特別複雜的互動，應快速提供一些初步的視覺回饋，讓使用者知道發生了什麼事。瀏覽器繪製的下一個影格是最佳機會
- 因此，INP 的意圖並非評估互動全部的最終效果 (例如經過網路請求和其他非同步作業的 UI 更新)，而是評估 next paint 遭到 block 的時間。延遲顯示視覺回饋可能會讓使用者誤以為網頁反應速度不夠快，因此出現了 INP，協助開發人員評估這部分的使用者體驗

## 什麼是 INP？
- INP 是一個量測標準，評估使用者在進行互動後多久後得到回饋
    - e.g. click、tap、keyboard 互動等等
- INP 會以最長的互動延遲時間回報，實際計算方式可以參考 [這裡](https://web.dev/articles/inp?hl=zh-tw#what-is-inp)
    - 『互動』是指同一個邏輯使用者手勢下觸發的一組事件處理
    - e.g 使用者觸碰螢幕畫面時會觸發多種事件，如 `pointerup`、`pointerdown`、`click` 等等，而互動內容可以透過 JavaScript、CSS 的瀏覽器的控制元素（e.g 表單）組合驅動
    - 『互動延遲』是以花最多時間的那個事件處理為標準。當使用者進行互動後，系統會執行一系列事件處理程序，而其中耗時最長的步驟將決定整體的延遲時間。只要某段程式執行特別久，瀏覽器就需要等它完成後才能更新畫面，這段等待時間就是互動延遲的核心瓶頸
        ![image](https://hackmd.io/_uploads/HJBrKSqS1e.png)

### 什麼是良好的 INP 分數？
- 針對行動裝置和桌面裝置，分別計算網頁載入時間的第 75 百分位數，這樣可以針對不同裝置的特性優化載入體驗
    - 如果 INP 低於或等於 200 毫秒，表示網頁反應速度良好
    - 如果 INP 超過 200 毫秒，或低於或等於 500 毫秒，表示網頁的回應速度需要改善
    - 如果 INP 超過 500 毫秒，表示網頁反應速度緩慢
- 雖然 INP 的標準分數對所有裝置都一樣，但分開評估能幫助你更清楚識別和解決不同裝置的問題

### 『互動內容』

- 互動活動的生命週期中，輸入延遲（Input Delay）發生在事件處理程序（Event Handlers）開始執行之前。這種延遲通常是由主執行緒上正在執行的長任務（Long Tasks）所引起，因為事件處理程序的回調（callbacks）必須等待這些任務完成後才能開始執行，進而導致在顯示下一個影格之前出現延遲
    ![image](https://hackmd.io/_uploads/SJ3A_B9rJx.png)
- 互動功能的主要驅動力通常是 JavaScript，但瀏覽器也能透過非 JavaScript 支援的控制項（non-JavaScript-driven controls）提供互動功能，例如核取方塊（checkbox）、圓形按鈕（radio button），以及由 CSS 支援的控制項（CSS-driven controls）。
- 為計算 INP，系統僅監控以下互動類型：滑鼠點選、觸控操作、按鍵輸入
    :::warning
    使用者也可以透過其他方式與網頁互動，例如懸停、縮放或捲動。我們不     會為了 INP 目的觀察這些互動。不過，這些互動行為的某些變化可能包     含INP 會評估的點擊、輕觸或按鍵動作等手勢
    :::
- 互動有可能出現在 Main Document 或是 iframe 裡面，而 iframe 裡面本身也需要使用 INP 才能正確評估使用者體驗。由於 JavaScript Web APIs 並不能存取 iframes 裡面的內容，可能會導致 [CrUX 和 RUM 之間的差異](https://web.dev/articles/crux-and-rum-differences?hl=zh-tw#iframes)

### INP 與 FID 的差異
- INP（Interaction to Next Paint）是 [FID（First Input Delay](https://web.dev/articles/fid)）的後繼指標，兩者都是衡量回應速度的指標，但存在以下差異：
    - FID：只測量頁面上的第一次互動延遲，主要用於評估頁面在載入時給使用者的第一印象
    - INP：觀察整個頁面生命周期中的所有互動，從輸入延遲（Input Delay）、事件處理執行時間（Event Handlers），到瀏覽器繪製下一個影格（Next Frame）的時間，提供更可靠的整體回應速度評估。
- 因此 INP 是更全面的回應性指標，不受互動發生時間限制

### 為什麼沒有回報任何 INP？
- 頁面載入，但使用者沒有進行任何動作
- 頁面載入，但使用者的互動方式不在量測範圍（e.g Scroll、Hover）
- 存取頁面的是機器人，並不屬於紀錄對象（e.g Search Crawler）

## INP Optimization

- 首先需找到造成差的 INP 之主要原因，也就是互動反應緩慢的地方
- 理想狀態應從 [Field Data (實地資料)](https://web.dev/articles/lab-and-field-data-differences#field_data) 開始提升 INP（透過造訪網頁的使用者進行評估 ），可以透過 RUM (Real User Monitoring) Provider 來得到更詳細的互動資訊
    - 可以參考 [這裡](https://web.dev/articles/diagnose-slow-interactions-in-the-lab) 找出 slow interactions
    - RUM Providers 如 Datadog、Grafana、Sentry
- 在取得顯示互動速度緩慢的實地資料後，才開始在 [實驗室](https://web.dev/articles/lab-and-field-data-differences?hl=zh-tw#lab_data) 重現問題，並進行下一步優化
- 可以將互動拆成三個階段，而他們加起來便是總互動延遲時間
    - 輸入延遲時間：從使用者開始與網頁互動開始計算，結束時間則是 event callback 開始執行時
    - 處理時間，包括 event callback 執行至完成所需的時間
    - 呈現延遲，即瀏覽器呈現下一個影格所需的時間，其中包含互動內容的視覺結果

> 在最佳化互動時，請務必瞭解每個瀏覽情境都會有自己的主執行緒。也就是說，top level 網頁會有主執行緒，但網頁上的每個 `<iframe>` 元素也會有自己的主執行緒。INP 會以 page level 回報，包括網頁上的任何慢速互動或該網頁中的任何 iframe。要確實瞭解互動發生在哪個影格，以便瞭解要查看哪個主要執行緒。不過，即使有多個主執行緒，資源受限的裝置仍可能會對這些看似獨立的執行緒造成影響

### 找出並減少輸入延遲
- 輸入延遲（Input Delay） 是使用者互動的第一階段，可能因以下原因導致延遲：
    - 主執行緒正在執行其他任務（例如腳本加載、解析、編譯）
    - 網路請求處理（Fetch Handling）
    - 計時器函數（Timer Functions）
    - 快速連續的其他互動導致執行緒重疊
- 啟動期間的腳本評估與長任務的關係
    - 頁面啟動期間，即使頁面已渲染，也不代表頁面完全可用，使用者可能在頁面加載未完成時互動
    - 腳本評估（Script Evaluation）可能延長輸入延遲，主要包括：
        - 解析腳本（Parsing）：檢查語法是否有效
        - 編譯腳本（Compiling）：將腳本轉為位元碼（Bytecode）
        - 執行腳本（Execution）：執行腳本邏輯
    - 如果腳本過大，會產生主執行緒上的長任務（Long Tasks），導致其他互動無法快速響應
- 減少輸入延遲的策略
    - 減少腳本大小以降低解析和編譯的負擔
    - 優化腳本加載順序（例如分割程式碼、懶加載）
    - 盡量避免在啟動階段執行長時間的 JavaScript 任務，讓主執行緒保持輕量化
- 更多內容可以參考『[最佳化輸入延遲時間](https://web.dev/articles/optimize-input-delay?hl=zh-tw)』和『[腳本評估和長時間工作](https://web.dev/articles/script-evaluation-and-long-tasks?hl=zh-tw)』

### 最佳化 event callback
- INP 不僅測量輸入延遲，還關注 event callback 能否快速完成執行
- 經常讓出主執行緒
    - 最佳優化建議是讓 event callback 中的工作量最少化，但若互動邏輯複雜，可能只能稍微減少工作量
    - 如果無法大幅減少 event callback 的工作量，可以將其拆分成多個任務，以避免長任務阻塞主執行緒，讓其他互動更快執行
    - 可以使用 setTimeout 將任務拆分成新任務，而傳進去的 callback 會在新工作中執行。更多可以參考這篇 [Optimize long tasks](https://web.dev/articles/optimize-long-tasks?hl=zh-tw)
        ```js
        function saveSettings () {
          // Do critical work that is user-visible:
          validateForm();
          showSpinner();
          updateUI();

          // Defer work that isn't user-visible to a separate task:
          setTimeout(() => {
            saveToDatabase();
            sendAnalytics();
          }, 0);
        }
        ```
- 讓渲染工作更快發生
    - 僅執行必要邏輯來更新下一影格的視覺效果，其他邏輯延後到後續任務完成
    - e.g. 在文字編輯器中，輸入文字後可能會發生很多事，但只要在下個影格先完成以下第一點就好
        1. 將使用者輸入的內容更新至文字方塊，並套用任何必要的格式設
        2. 更新 UI 中顯示目前字數的部分
        3. 執行邏輯，檢查拼寫錯誤
        4. 儲存最新變更 (在本機或遠端資料庫中)
        > 雖然在 `requestAnimationFrame` 中使用 `setTimeout` 的做法有點難懂，但的確是有效的做法（且適用於所有瀏覽器
        ```js
        textBox.addEventListener('input', (inputEvent) => {
          // Update the UI immediately, so the changes the user made
          // are visible as soon as the next frame is presented.
          updateTextBox(inputEvent);

          // Use `setTimeout` to defer all other work until at least the next
          // frame by queuing a task in a `requestAnimationFrame()` callback.
          requestAnimationFrame(() => {
            setTimeout(() => {
              const text = textBox.textContent;
              updateWordCount(text);
              checkSpelling(text);
              saveChanges(text);
            }, 0);
          });
        });
        ```
        ![image](https://hackmd.io/_uploads/H1Uo2tjrJg.png)
- 避免版面配置衝突 (Layout Thrashing)
    - 有時又稱為『強制同步版面配置 (Forced Synchronous Layout)』，是一種 rendering 效能問題
    - 發生在透過 JavaScript 更新樣式並於同一件工作中讀取樣式時發生，而 [JavaScript 中有許多屬性可能會導致 layout thrashing](https://gist.github.com/paulirish/5d52fb081b3570c81e3a)
    - 涉及 layout thrashing 的 render 工作會在右上角顯時紅色三角形，通常被標記 `Layout` 或 `Recalculate Style`
        ![image](https://hackmd.io/_uploads/HJ068A3Hyx.png)
    - 系統立即在 JavaScript 中請求這些 value，導致瀏覽器必須同步執行 layout 作業，這項作業原本可以等到 event callback 執行完畢再以非同步的方式執行
    - 對 Layout Thrashing 更詳細的資訊可以參考 [Avoid large, complex layouts and layout thrashing](https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing)

### 盡量縮短呈現延遲時間（Presentation Delay）
> 指 event handler 完成執行後，直到瀏覽器能繪製出下一個影格顯示視覺變更之間的時間
- 盡量縮小 DOM 大小
    - DOM 越大，Render 工作所需要的時間就會越長（但並非線性關係）
    - 龐大的 DOM 會有以下兩種問題
        - 在頁面 Initial Render 時，需要較長時間來 render 完頁面的初始狀態
        - 在回應 User Interaction 時，DOM 的更新成本變高，進而增加瀏覽器繪製出下一影格所需的時間
    - 雖然能透過 [Flatten DOM](https://web.dev/articles/dom-size-and-interactivity?hl=zh-tw#how_can_i_reduce_dom_size) 或在 [使用者互動期間新增 DOM](https://web.dev/articles/dom-size-and-interactivity?hl=zh-tw#consider_an_additive_approach) 來保持 Initial DOM 的大小，但某些情況 DOM 仍無法大幅縮減
    - 進一步瞭解： [DOM 大小和互動性](https://web.dev/articles/dom-size-and-interactivity?hl=zh-tw)
- 透過 CSS `content-visibility` 只對可視範圍內的元素進行 render 工作，詳細使用方式可以參考 [content-visibility: the new CSS property that boosts your rendering performance](https://web.dev/articles/content-visibility)
- 使用 JavaScript render HTML 時，需留意效能成本
    - 瀏覽器解析 HTML 形成 DOM，應用樣式並計算佈局，最後渲染頁面，這是無法避免的性能成本
    - HTML 是以串流 (streaming) 形式從伺服器傳回，瀏覽器會分段解析並逐步渲染，利用內建優化來提升性能
    - Single Page Application (SPA)
        - 首次載入僅送回最少量的 HTML，主要內容由 JavaScript 動態渲染，後續互動也由 JavaScript 更新內容區域
        - 這種模式額外增加 JavaScript 的處理成本（生成與解析 HTML），並延遲瀏覽器的渲染，特別是在處理大量 HTML 時
        - client side 渲染過多 HTML 可能導致下一影格渲染延遲，影響網站的響應速度
    - 過多的 JavaScript 渲染會阻塞主執行緒，導致用戶輸入無法快速響應，需謹慎處理此效能問題
    - 即使非 SPA 網站，也可能因互動需要在客戶端渲染部分 HTML，因此仍須避免一次性渲染大量 HTML
    - 更多 Client Side Rendering 可以參考 [Client-side rendering of HTML and interactivity](https://web.dev/articles/client-side-rendering-of-html-and-interactivity)
## 參考來源
- [web.dev - Interaction to Next Paint (INP)](https://web.dev/articles/inp)
- [web.dev - optimize-inp](https://web.dev/articles/optimize-inp)
- [web.dev - Find Slow Interactions in the Field](https://web.dev/articles/find-slow-interactions-in-the-field?hl=zh-tw)