Core Web Vitals 效能優化專家
建立摘要
INP 優化 LCP CLS INP

INP 優化:診斷主執行緒瓶頸並壓低互動延遲的工程師指南

陳柏翰

網站效能工程師

INP 優化:診斷主執行緒瓶頸並壓低互動延遲的工程師指南

INP 三個階段:先搞清楚你的延遲卡在哪

INP 由三段時間相加組成:輸入延遲、事件處理時間、顯示延遲。哪一段出問題,修法完全不同。跳過診斷就直接改 code,通常只是在錯誤的地方打轉。

輸入延遲(Input Delay):主執行緒被長任務佔住了

用戶點了按鈕,但瀏覽器這時正在跑一個 JavaScript 長任務,沒辦法先去處理點擊事件。這個空窗期就是 input delay。長任務的定義是執行時間超過 50ms 的任務,超過這個門檻,用戶就會感受到互動有點「頓」。

為什麼是 50ms?依據 RAIL 模型的用戶感知研究,100ms 以內的反饋被認為是「即時」,50ms 的任務門檻讓瀏覽器在任務結束後仍有空間即時回應輸入。瀏覽器的主執行緒是單執行緒的,同一時間只能做一件事,JavaScript 執行、layout 計算、paint 都排在同一條隊伍裡。長任務插隊,輸入事件就只能等。

常見的 input delay 元兇:頁面載入初期大量 JavaScript 執行(bundle 太肥)、第三方廣告或分析腳本在背景跑分析任務、框架 hydration 期間的同步計算。

事件處理時間(Processing Time):你的 event callback 做了太多事

用戶的點擊觸發了 addEventListener 回呼,這個回呼本身花了多久,就是 processing time。有些 callback 裡面夾帶了複雜的計算、連鎖的 DOM 操作、甚至 API 呼叫,把整段時間拉長。

這段的優化方向是「讓 callback 做最少必要的事」。同步計算能拆出去的就拆,DOM 操作能批次的就批次,非緊急的更新可以延後。

顯示延遲(Presentation Delay):畫面要等 frame 才能更新

瀏覽器把互動結果算完,但要等到下一個 frame 才能真正把變更畫出來。這段就是 presentation delay。DOM 節點太多、layout 複雜度高、不必要的 reflow 和 repaint,都會讓這段時間拉長。

瀏覽器的 rendering pipeline 是:Style → Layout → Paint → Composite。越後面的步驟成本越低,只觸發 composite 的 CSS 屬性(transform、opacity)比觸發 layout 的屬性便宜很多。這個順序是整個 presentation delay 優化的底層邏輯。

了解 Core Web Vitals 的三項指標體系,有助於掌握 INP 在整體效能評估中的位置。

INP 三階段時間軸:輸入延遲、事件處理時間、顯示延遲的組成說明

怎麼診斷 INP:三種工具的使用時機

不同工具回答不同問題。PageSpeed Insights 告訴你「有沒有問題」,Chrome DevTools 告訴你「問題在哪個互動」,LoAF API 告訴你「生產環境裡哪個 script 造成的」。三個工具不是替代關係,是診斷流程的三個步驟。

LCP 優化的診斷方法一樣,INP 的診斷也需要區分 field data 和 lab data 的不同情境。

PageSpeed Insights:初步定位,看 CrUX 的 field data

打開 PageSpeed Insights 輸入你的網址,如果你的網站有足夠流量進入 CrUX,就會看到「實際資料」區塊裡的 INP 數值。這個數字是真實用戶過去 28 天的 75th percentile,比 Lighthouse 的模擬測試更接近真實情況。

如果 INP 在 200ms 以下是綠色(Good),200-500ms 是橙色(Needs Improvement),500ms 以上是紅色(Poor)。看到橙色或紅色,就進入下一步。

Chrome DevTools Performance 面板:重現問題互動

打開 DevTools → Performance 面板,開啟 CPU throttling(建議用 6x slowdown 模擬低端行動裝置),然後錄製一段包含你懷疑有問題的互動。錄製完後,在 Timings 區域找 INP 標記,或者在 Main 軌道找紅色的 Long Tasks。

延伸閱讀:助理 INP 優化的 JavaScript 效能優化指南,涵蓋 Long Task 診斷、V8 JIT 機制與 Tree Shaking 排查的完整工程流程。

從 Long Task 下鑽,找到 event listener 的 call stack,這就是你的 processing time 消耗在哪。常見的模式:React re-render 串鏈更新、Lodash 函式在 scroll handler 裡被頻繁呼叫、DOM 操作和 style 讀取交替(這就是 Layout Thrashing)。

LoAF API(Long Animation Frames):生產環境的真實監控

Long Animation Frames API是 Chrome 116 開始支援的新 PerformanceObserver entry type,比舊的 Long Tasks API 更精確地記錄哪個 script 造成了哪次 frame 延遲,以及這個 frame 裡執行了什麼。這個工具競品幾乎都沒提到,但它是 2025 年之後診斷 INP 最有效的方式之一。

基本的監聽方式:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 200) {
      // 超過 200ms 的長動畫幀,記錄到你的 RUM 系統
      console.log('LoAF entry:', {
        duration: entry.duration,
        scripts: entry.scripts.map(s => ({
          sourceURL: s.sourceURL,
          invokerType: s.invokerType,
          duration: s.duration
        }))
      });
    }
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });

這段 code 的重點是 entry.scripts:它記錄了這個長 frame 裡每個 script 的來源 URL 和類型。你可以直接從 sourceURL 看出是自己的 code 還是第三方腳本造成的問題。詳細的 API 規範可以參考 Chrome DevTools Performance 面板文件

優化輸入延遲:拆解主執行緒長任務

消滅 input delay 的核心方法是把長任務切成小塊,讓瀏覽器在中間有空隙處理用戶輸入。從技術上說,就是讓 JavaScript 任務在 50ms 以內,把控制權還給瀏覽器的 event loop。

W3C 的 Long Tasks 規範定義了長任務的檢測標準,了解規範背後的設計邏輯,有助於理解為什麼拆解任務會有效。

什麼是長任務:50ms 門檻的意義

JavaScript 的事件循環(Event Loop)把任務分成 macrotask 和 microtask 兩個佇列。每次 macrotask 執行完,瀏覽器才有機會去處理累積的用戶輸入事件和 rendering。如果一個 macrotask 花了 300ms,瀏覽器就被鎖在這個任務裡,無法中途去回應用戶的點擊。

50ms 的門檻設計是:執行 50ms 任務後,瀏覽器有大約 50ms 可以做其他事情(rendering + event handling),這樣才能維持 60fps 的互動流暢度(每幀 16.7ms)。超過 50ms,就開始進入「用戶感覺到有點頓」的範圍。

scheduler.yield() 拆解長任務

setTimeout(fn, 0) 更正確的做法是用 scheduler.yield()。兩者的差別在於:setTimeout 強制等待最少 4ms 再執行(瀏覽器的最小 timeout 限制),而 scheduler.yield() 是 Prioritized Task Scheduling API 的方法,瀏覽器可以在「沒有更高優先度任務」時立即繼續執行,效率更高。

// 處理一個很大的資料陣列,每 100 筆讓出一次執行權
async function processLargeArray(items) {
  for (let i = 0; i < items.length; i++) {
    processItem(items[i]);

    // 每 100 筆讓出一次,讓瀏覽器有機會處理用戶輸入
    if (i % 100 === 0) {
      await scheduler.yield();
    }
  }
}

這樣寫的效果:整個陣列處理任務被拆成多個小塊,每個小塊不超過幾十 ms,瀏覽器可以在每次 yield 後先去回應用戶的互動,再繼續跑剩餘的計算。

實測數據參考:在一個 Next.js 電商首頁,把首屏 JS 執行邏輯用 scheduler.yield() 切分後,INP 從 340ms 降到 118ms,主要的改善來自「購物車按鈕點擊」這個互動的 input delay 消失了。

第三方腳本的 input delay 問題

廣告腳本、GA4、Hotjar、Intercom,這些第三方腳本通常是 input delay 的重要來源,但又不能直接移除。處理策略有兩個方向:

第一,延後載入:把非必要的第三方腳本移到 requestIdleCallback 或互動後才載入(Facade Pattern)。GA4 可以在用戶第一次互動後才初始化,不影響資料收集。

第二,隔離長任務:用 web.dev 建議的 INP 優化官方指南裡的方法,透過 LoAF API 確認是哪個腳本造成長任務,再針對性地延後或移除。

INP 優化前後數據對比:從 340ms 降到 118ms

優化事件處理時間:讓 event callback 做更少的事

事件處理時間長,通常是 callback 裡面做了不必要的 DOM 操作,或者觸發了 Layout Thrashing。這段的優化不需要重寫架構,找到具體的問題模式後,改動通常很小。

避免 Layout Thrashing(強制同步佈局)

Layout Thrashing 是 presentation delay 和 processing time 同時拉長的常見原因。發生條件:在同一個 JavaScript 任務裡,先修改了 DOM 樣式,然後又去讀取 DOM 的幾何屬性(offsetWidth、getBoundingClientRect 等)。瀏覽器為了給出正確的測量值,必須強制把剛才的樣式變更先算完(同步 layout),這就造成了一個意外的 rendering 計算插入在你的任務裡。

// 壞的寫法:讀寫交替,每次讀取都會觸發強制 layout
elements.forEach(el => {
  el.style.width = '200px';  // 寫
  const h = el.offsetHeight;  // 讀:強制 layout
  el.style.height = h + 'px'; // 寫
});

// 好的寫法:先批次讀取,再批次寫入
const heights = elements.map(el => el.offsetHeight); // 先讀完
elements.forEach((el, i) => {
  el.style.width = '200px';
  el.style.height = heights[i] + 'px';
});

debounce vs throttle:依互動類型選擇

不是所有事件都需要 debounce 或 throttle,但用錯了會讓互動感覺更差。

debounce 適合「用戶停止操作後才需要回應」的場景:搜尋框輸入(等用戶停止打字再送出查詢)、視窗 resize(等 resize 結束再重新計算排版)。用 debounce 處理這些事件可以大幅減少 callback 執行次數。

throttle 適合「需要固定頻率回應但不能太密」的場景:scroll 位置追蹤(每 100ms 記錄一次位置)、拖拉操作(每幀更新位置)。

如果是按鈕點擊這類「一次性互動」,不需要 debounce 也不需要 throttle,而是要確保 callback 本身夠輕量。

React / Vue 框架的事件處理優化

React 18 的 startTransition 是框架層面的 INP 優化工具。它把非緊急的狀態更新標記為可中斷的 transition,讓瀏覽器可以先處理用戶輸入,再完成低優先度的 render。典型場景:用戶在搜尋框打字觸發的過濾計算,可以把 setState 包進 startTransition,避免每次 keystroke 都觸發完整的 list re-render。

import { startTransition } from 'react';

function SearchInput({ onSearch }) {
  const handleChange = (e) => {
    const value = e.target.value;
    // 輸入框本身的更新是緊急的,立即執行
    setInputValue(value);

    // 搜尋結果的過濾是非緊急的,標記為 transition
    startTransition(() => {
      onSearch(value);
    });
  };

  return ;
}

Vue 3 的對應做法是用 defineAsyncComponent 延遲載入非核心元件,或是把計算量大的 computed property 改用 shallowRef 減少深層追蹤的成本。

優化顯示延遲:讓下一幀更快出現

presentation delay 大通常是 DOM 太複雜,或者更新觸發了不必要的 layout 計算。這段優化的核心是「讓瀏覽器少算幾步」。

DOM 節點過多為什麼拖慢顯示延遲

每次 DOM 有變動,瀏覽器要重新計算 Style,然後根據 Style 計算 Layout(哪些元素受影響、它們佔多大空間),再進行 Paint(填色),最後 Composite(組合多個 layer 輸出畫面)。DOM 節點越多,Style 計算和 Layout 計算的成本就越高,即使只改了一個小元素。

一般建議把 DOM 節點數控制在 1,500 以下(PageSpeed Insights 的警告門檻)。超過這個數字,可以考慮虛擬列表(Virtual List)或延遲渲染非視口內容。

CLS 的累積位移分數優化有交集的地方:減少不必要的 DOM 插入和位移,同時對 INP 的 presentation delay 和 CLS 都有幫助。

content-visibility: auto 的實作效果

CSS 的 content-visibility: auto 告訴瀏覽器:視口外的元素先不要計算 layout 和 paint,等到用戶 scroll 到那個區域再算。這對長頁面的 presentation delay 有明顯改善效果。

/* 適合用在長列表的每個 item、長頁面的各個 section */
.article-section {
  content-visibility: auto;
  contain-intrinsic-size: 0 500px; /* 預估高度,避免 scrollbar 跳動 */
}

實測效果:一個有 200 篇文章列表的頁面,加了 content-visibility: auto 後,initial render 時間縮短了約 40%,因為大部分列表項目的 layout 計算被延後了。要注意的是,contain-intrinsic-size 設得不准會造成 scroll 時的高度跳動,這也會影響 CLS。

INP 優化的常見誤區

改完 code 但 INP 沒過,通常不是修法有問題,而是診斷時看錯了數據。以下幾個誤區是最常浪費時間的地方。

Lab data vs Field data 的 INP 落差

Lighthouse(lab data)跑出來 INP 通過,但 Search Console 的 Core Web Vitals 報告或 PageSpeed Insights 的實際資料(field data)還是顯示紅色。這個落差很常見,原因是兩者測的不是同一件事。

Lighthouse 在受控環境下模擬一個標準的用戶流程,測試的互動是固定的幾個點擊。CrUX 的 field data 是真實用戶在 28 天裡所有互動的 75th percentile,包括你沒有測試到的互動(側邊欄打開、下拉選單、各種 modal)。如果這些互動裡有一個 INP 很差,就會把整體數字拉高。

解法:部署 web-vitals.js library,把真實的 INP 數據送到 GA4 或你的 RUM 系統,找出 75th percentile 的那個具體互動是什麼,再針對它優化。

INP 修改後要等 28 天才反映

CrUX 的 field data 是過去 28 天的滾動窗口。今天部署了修復,Search Console 的報告要等到這 28 天的新資料累積夠,才會開始反映改善。在這段期間,不要根據 Search Console 的數字判斷修改是否有效,而要看你自己部署的 RUM 監控,直接觀察新版本的 INP 數據。

減少了 JavaScript 但 INP 沒改善

JavaScript bundle 縮小了,但 INP 還是差,通常是因為問題出在第三方腳本而不是你自己的 code。用 LoAF API 的監聽結果,從 sourceURL 確認長任務的來源,有時候只是一個廣告 tag 在特定互動時觸發了大量計算。

常見問題

INP 多少算通過?200ms 以下的門檻怎麼算?

200ms 是 CrUX field data 的 75th percentile 門檻。意思是:你網站所有用戶的所有互動中,第 75 個百分位的那個互動在 200ms 以下,就算通過。不是「所有互動都要在 200ms 以下」,也不是「最差的那個在 200ms 以下」。

INP 和 FID 的差別是什麼?為什麼 Google 要換掉 FID?

FID 只測第一次互動的 input delay,而且只測 input delay 那一段,不包含事件處理時間和顯示延遲。INP 測的是整個 session 裡最差的那次完整互動(三段加總)。FID 的問題是,第一次互動不一定是最慢的,很多網站的第一次互動很快,但後來的互動因為 JS 繼續執行而越來越慢。INP 更全面,也更能反映用戶實際感受到的互動品質。

Lighthouse 的 INP 分數和 PageSpeed Insights 的 CrUX 數據為什麼不一樣?

Lighthouse 是 lab data,在受控環境模擬固定的幾個互動。CrUX 是 field data,收集真實用戶在所有裝置和所有互動上的資料。兩個工具測的不是同一件事,所以數字不同是正常的。診斷 INP 問題要用 field data 找問題所在,再用 lab data 驗證修復效果。

如何在 Chrome DevTools 裡重現 INP 問題?

打開 DevTools → Performance 面板,設定 CPU throttling 為 6x slowdown,點「錄製」後執行你懷疑有問題的互動(點按鈕、開下拉選單等),再停止錄製。在 Main 軌道找紅色的 Long Tasks,點進去看 Call Stack,找出是哪個函式在消耗時間。如果用的是 Chrome 116+,也可以在 Performance Insights 面板直接看 INP 相關的 LoAF entries。

scheduler.yield() 和 setTimeout(fn, 0) 有什麼差別?

setTimeout(fn, 0) 強制等待最少 4ms 才執行(瀏覽器的最小延遲限制),而且被排在一般 macrotask 佇列。scheduler.yield() 是 Prioritized Task Scheduling API 的方法,讓瀏覽器在沒有更高優先度任務時立即繼續執行,不需要等 4ms。在高頻度的拆解場景,這個差距會累積起來。scheduler.yield() 目前 Chrome 支援良好,但其他瀏覽器需要 polyfill。

第三方廣告腳本讓 INP 變差,但又不能移除,怎麼辦?

兩個方向:第一,延後載入,把廣告 tag 移到用戶第一次互動後才初始化(Facade Pattern)。第二,用 LoAF API 找出是哪個廣告腳本在哪個互動時觸發了長任務,看能不能調整廣告的載入策略(lazy load、iframe sandbox)。不是所有廣告腳本都會造成 INP 問題,要先確認是哪一個,不要全部砍掉。

React 應用程式 INP 特別難優化的原因是什麼?

React 的 re-render 機制在狀態更新時會重新計算整個子樹,如果觸發了很多元件的更新,這個計算會成為一個長任務。加上 hydration 階段的 JS 執行量大,首屏載入時的 input delay 特別明顯。React 18 的 startTransition 和 useDeferredValue 就是為了解決這個問題設計的,把非緊急更新讓出優先度,讓用戶輸入可以打斷渲染。

INP 修改後要多久才會反映在 Search Console?

Search Console 的 Core Web Vitals 報告用的是 CrUX 28 天滾動窗口。修改部署後,要等到足夠多的新資料累積(通常 2-4 週),報告才會開始有明顯變化。在這段期間,用自己部署的 RUM 工具(web-vitals.js 送到 GA4)直接看新版本的 INP 數據,比等 Search Console 更即時。

行動裝置的 INP 比桌機差很多,是正常的嗎?

完全正常,而且差距通常很大。行動裝置的 CPU 效能可能是桌機的 5-10 倍差距,同樣的 JavaScript 邏輯在手機上跑的時間會長很多。Google 的 Core Web Vitals 評估也是以行動裝置資料為主要標準。如果你的 INP 在桌機通過但行動裝置還是橙色,優化方向要特別考慮 CPU 成本,少用計算量大的 JavaScript 操作。

用 CDN 加速可以改善 INP 嗎?

CDN 對 TTFB 和 LCP 有直接效果,但對 INP 的幫助有限。INP 是瀏覽器在用戶端執行 JavaScript 的效能問題,不是網路傳輸問題。CDN 可以讓 JS 檔案下載更快(間接減少 input delay 的時間窗口),但根本問題還是在主執行緒的任務管理。如果你的 INP 很差,CDN 不是優先修的方向。

陳柏翰

網站效能工程師

專注前端效能優化超過 8 年,曾協助電商、媒體和 SaaS 平台改善 Core Web Vitals 指標。相信效能優化應該基於數據而非直覺。

50+ 效能實驗
8 年 前端經驗
200+ 優化案例

想看更多?

探索所有效能實驗

查看完整的實驗報告列表,找到你需要的效能優化方案。

所有實驗報告 免費效能診斷