什麼是 INP?為何它如此重要?
Interaction to Next Paint (INP) 是 Google Core Web Vitals 中用於衡量網頁「互動回應性」的核心指標。2024 年,INP 正式取代了 First Input Delay (FID),標誌著網頁效能評估從「首次互動」轉向「全生命週期互動體驗」的重大轉變。
與 FID 僅關注使用者第一次互動的延遲不同,INP 監測使用者在整個瀏覽過程中的所有合格互動,並報告一個能代表整體體驗的延遲值。這迫使開發者必須深入理解瀏覽器主執行緒調度、JavaScript 事件循環機制,以及渲染管線的運作原理。
INP 的測量原理與瀏覽器底層機制
Event Timing API 與互動生命週期
INP 的測量基於 W3C 規範的 Event Timing API。一個標準的 INP 互動週期由三個關鍵階段組成:
| 階段 | 說明 | 常見瓶頸 |
|---|---|---|
| 輸入延遲 (Input Delay) | 從使用者操作開始,到瀏覽器開始執行事件回調的時間 | 主執行緒被長任務阻塞 |
| 處理時間 (Processing Duration) | 執行所有事件回調函式所需的總時間 | JS 邏輯複雜、框架開銷 |
| 呈現延遲 (Presentation Delay) | 回調執行完畢到下一幀繪製完成的時間 | DOM 規模過大、CSS 複雜 |
📊 關鍵數據:研究顯示,呈現延遲平均佔總 INP 延遲的 42%,是最容易被忽視卻影響最大的階段。
納入計算的互動類型
INP 專注於預期會觸發視覺回饋的離散操作:
- ✅ 滑鼠點擊 (Click)
- ✅ 觸控裝置輕觸 (Tap)
- ✅ 鍵盤按鍵 (Keypress)
- ❌ 懸停 (Hover) — 不計入
- ❌ 滾動 (Scrolling) — 由合成器執行緒處理,不計入
聚合邏輯與效能分級
INP 的最終數值並非所有互動的平均值,而是採用以下邏輯:
| 互動次數 | 計算方式 |
|---|---|
| < 50 次 | 取延遲最長的互動 |
| ≥ 50 次 | 取第 98 百分位數 |
效能分級標準:
| 等級 | INP 值 | 使用者感受 |
|---|---|---|
| 🟢 良好 | ≤ 200ms | 反應迅速,體驗流暢 |
| 🟡 需改善 | 200-500ms | 輕微延遲,尚可接受 |
| 🔴 不佳 | > 500ms | 明顯卡頓,影響體驗 |
輸入延遲 (Input Delay) 的優化工程
輸入延遲發生在使用者互動開始,但瀏覽器尚未能執行對應事件處理函式之時。核心優化目標是解放主執行緒。
分解長任務 (Breaking Up Long Tasks)
JavaScript 的單執行緒特性意味著超過 50ms 的任務會阻塞主執行緒。解決方案是將龐大任務拆解為多個小任務,並在任務之間「讓出」主執行緒。
setTimeout vs scheduler.yield() 比較
| 方法 | 優點 | 缺點 |
|---|---|---|
setTimeout(..., 0) | 瀏覽器支援度高 | 後續任務推至佇列尾端,執行順序不可控 |
scheduler.yield() | 後續執行排在佇列前端,具優先級繼承 | 較新的 API,需考慮相容性 |
優先級繼承的重要性:若原本任務是使用者互動觸發的高優先級任務,scheduler.yield() 後續部分仍保持高優先級,而 setTimeout 產生的新任務僅具預設優先級。
實作範例:處理大量數據
async function processLargeData(data) { for (const item of data) { processItem(item);
if (shouldYield()) { // 優先使用 scheduler.yield if (globalThis.scheduler?.yield) { await scheduler.yield(); } else { // Fallback await new Promise(resolve => setTimeout(resolve, 0)); } } }}避免計時器濫用
| 問題模式 | 優化策略 |
|---|---|
高頻 setInterval 動畫 | 改用 requestAnimationFrame |
| 密集輪詢檢查 | 使用 IntersectionObserver、MutationObserver |
| 低優先級背景工作 | 使用 requestIdleCallback |
管理第三方腳本
第三方腳本(廣告、分析、客服機器人)是主執行緒擁塞的常見元兇。
隔離策略:
| 策略 | 適用場景 |
|---|---|
| Web Workers | 非 UI 相關邏輯(數據分析、加密運算) |
| 延遲加載 | 非首屏關鍵腳本,使用 defer/async 或動態注入 |
處理時間 (Processing Duration) 的優化
處理時間是事件回調函式本身的執行時間,取決於 JavaScript 邏輯複雜度與前端框架開銷。
核心原則
在事件處理函式中,只做與「視覺回饋」最直接相關的最小必要工作。
案例:搜尋框輸入優化
❌ Bad Pattern:
input.addEventListener('input', (e) => { validateInput(e.target.value); // 耗時驗證 fetchResults(e.target.value); // API 請求 updateUI(e.target.value); // 重繪 DOM});✅ Good Pattern (Debounce & Defer):
input.addEventListener('input', (e) => { // 1. 立即更新輸入框視覺狀態 updateInputDisplay(e.target.value);
// 2. Debounce 延後複雜處理 debouncedSearch(e.target.value);});避免佈局抖動 (Layout Thrashing)
佈局抖動發生在 JavaScript 連續交錯「讀取」與「寫入」樣式時,瀏覽器被迫在單一幀內多次重新計算佈局。
❌ 錯誤範例:
function resizeItems() { for (let item of items) { let width = item.offsetWidth; // 讀取 → 強制 Reflow item.style.width = (width + 10) + 'px'; // 寫入 → 標記佈局失效 }}✅ 優化範例(讀寫分離):
function resizeItems() { // 1. 批量讀取 let widths = items.map(item => item.offsetWidth);
// 2. 批量寫入 items.forEach((item, index) => { item.style.width = (widths[index] + 10) + 'px'; });}呈現延遲 (Presentation Delay) 與渲染管線優化
呈現延遲涉及 Style → Layout → Paint → Composite 的完整渲染流程。DOM 規模與 CSS 複雜度是此階段的決定性因素。
DOM 規模的影響
大型 DOM 樹(超過 1500 個節點)會顯著增加樣式計算與佈局時間。
CSS content-visibility 的應用
.card { content-visibility: auto; contain-intrinsic-size: 100px; /* 提供佔位高度 */}此屬性允許瀏覽器「跳過」視口外元素的渲染工作,對長列表或無限滾動頁面是極致優化手段。
| 特性 | 說明 |
|---|---|
| 無障礙性 | 內容仍在 Accessibility Tree 中可見 |
| SEO | 搜尋引擎可索引這些內容 |
| 頁面搜尋 | 支援 Find-in-page 功能 |
客戶端渲染 (CSR) 的緩解策略
| 策略 | 說明 |
|---|---|
| 虛擬滾動 | 僅渲染可見區域 DOM 節點 |
| 分時渲染 | 利用 requestAnimationFrame 分批插入節點 |
總結:INP 優化檢查清單
| 階段 | 優化重點 |
|---|---|
| 輸入延遲 | 分解長任務、使用 scheduler.yield()、管理第三方腳本 |
| 處理時間 | 最小化回調邏輯、Debounce/Throttle、避免佈局抖動 |
| 呈現延遲 | 控制 DOM 規模、使用 content-visibility、虛擬滾動 |
優化 INP 不僅是為了通過 Google 指標考核,更是為使用者提供尊重其時間與意圖的卓越體驗。透過系統性的工程方法——從建立觀測能力、精細化排程,到擁抱現代框架特性——開發團隊能在競爭激烈的數位環境中脫穎而出。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。