# 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)