2025年,全球網站在行動版INP的及格率來到77%,比前一年的74%再往上一步;但把範圍縮到全球流量前1,000大的網站,過關的卻只剩63%。規模越大、功能堆得越多,反而越難把互動速度控制在及格線內。
INP(Interaction to Next Paint,與下一個畫面顯示的互動)量的就是這件事:你點下按鈕、滑動選單、打字送出表單,到瀏覽器把畫面回饋畫出來,中間卡了多久。自2024年3月起,它取代FID成為Core Web Vitals的正式成員,互動順不順已經直接算進Google的排名考量。
好消息是,INP不是玄學,它可以被拆解、被量測、被一段一段修好。要優化,得先弄清楚使用者等的這段時間,到底卡在哪一段。
INP 是什麼?跟被取代的 FID 有什麼差異?
INP是一個衡量網頁對使用者互動反應有多快的指標。它會觀察你在這個頁面上的每一次點擊、輕觸螢幕、按鍵,各自等了多久才等到畫面回饋,最後回報其中最慢(或接近最慢)的那一次當作這頁的分數。簡單來說,它量的不是隨便挑一次互動,而是整頁互動體驗能不能穩定信賴。
要特別說清楚,INP只算三種互動:滑鼠點擊、觸控螢幕的輕觸、實體或螢幕鍵盤的按鍵。滑鼠懸停與單純的捲動不算在內,所以捲動動畫做得再流暢,對INP分數本身沒有直接幫助,除非捲動事件監聽器順手把主執行緒卡住,那就是另一回事了。
它跟前一代的FID(First Input Delay,首次輸入延遲)差在哪?關鍵有兩點。測量範圍:FID只算到瀏覽器開始處理事件為止,INP一路算到下一個畫面真正畫出來。測量次數:FID只測「第一次」互動,INP則觀察整趟造訪裡的所有互動。第一次互動往往不是使用者體驗最差的那次,只看第一印象容易報喜不報憂,這也是Google在2024年3月讓INP正式取代FID、成為Core Web Vitals一員的原因。
INP 要多快,才算通過門檻?
先講結論,INP的及格線是200毫秒,而且看的是實際使用者資料的第75百分位數,行動版和桌機版分開計算。Google用三個顏色把回應速度分成三段:綠色(良好)是200毫秒以下,代表頁面對絕大多數互動都能快速回應;橘色(需要改善)落在200到500毫秒之間,使用者開始感覺得到延遲;紅色(不佳)超過500毫秒,互動明顯卡頓,體驗已經受損。
為什麼看第75百分位數,而不是平均值?因為平均會被少數很快的互動稀釋掉,沒辦法反映多數人的真實感受;第75百分位數的意思是「至少四分之三的使用者體驗有這麼好或更好」,誠實得多。
這條線看起來嚴格,但實際分布很兩極。根據HTTP Archive《2025 Web Almanac》的最新調查,2025年行動版網站的INP良好比例來到77%,比2024年的74%再進步一截;桌機版更高,達到97%。可是把範圍縮到全球流量前1,000大的網站,過關比例卻只有63%,雖然比2024年的53%已經明顯拉近差距(從落後平均21個百分點,收斂到只落後14個百分點),但越大、越複雜、塞越多第三方腳本的網站,確實還是更容易在這裡失分。同一份調查也指出,桌機與行動版的總阻塞時間中位數分別是約92毫秒與1,916毫秒,裝置效能的落差,正是INP在行動版特別容易失分的原因之一。

光知道及格線還不夠,要修就得先知道延遲卡在哪一段。
一次互動的延遲,其實是三段時間加起來的
每一次互動的延遲,其實是三段時間加起來的總和,分別是輸入延遲、處理時間、呈現延遲。要優化INP,第一步永遠是先搞清楚自己慢在哪一段,再對症下藥,而不是把所有招式一股腦全用上。

輸入延遲是從你動作的那一刻起,到瀏覽器能開始執行對應事件處理常式為止的空窗;它多半是因為主執行緒當下正忙著跑別的工作,你的點擊只能排隊等,這也是Long Task最直接傷害的一段。處理時間是瀏覽器執行所有相關回呼、把該算的算完、該改的狀態改完所花的時間;事件處理常式裡的JavaScript越肥、DOM操作越複雜,這段就拖越久。呈現延遲則是回呼都跑完之後,瀏覽器把更新後的畫面真正繪製到螢幕上所花的時間;版面重算的範圍越大、要重繪的元素越多,這段就越長。
三段加起來,才是使用者實際感受到的那一下延遲。這個拆法的價值在於,它把「網站好慢」這種模糊抱怨,變成三個可以分別下手的具體目標。實務上,輸入延遲和處理時間通常是大頭,而它們背後最常見的共同元凶,就是接下來要講的Long Task。
Long Task 為什麼是拖垮 INP 的頭號戰犯?
Long Task指的是任何在主執行緒上跑超過50毫秒的工作。要理解它為什麼傷害這麼大,得先認識主執行緒這個角色。
主執行緒是瀏覽器裡負責跑絕大多數事情的那條線,解析HTML、計算CSS、執行JavaScript,幾乎都擠在這一條上,而它有個硬限制,一次只能做一件事。當一件工作占著主執行緒不放,所有後面的事情,包括你剛剛點下去的那個按鈕,都只能排在後面乾等。50毫秒這條線是這樣畫出來的。只要一件工作超過50毫秒,超出的部分就算成它的阻塞時間,這段時間瀏覽器完全沒辦法回應任何互動。
最容易踩的坑,是工程上看起來分工清楚、執行起來卻是一整塊的程式碼。假設有個函式在使用者按下「送出訂單」時被觸發,依序做了「檢查庫存、扣款、寫入訂單資料庫、更新畫面、送出分析事件」這五件事。從程式架構看,拆成五個函式各司其職,好維護也好除錯;但瀏覽器不在乎你怎麼分函式,它把這五件事當成一件工作連續跑完。只要其中任何一件稍微吃重,整塊就輕鬆超過50毫秒,變成一個Long Task。

結果就是,使用者按下「送出訂單」,得等這一整塊跑完,瀏覽器才有空把回饋畫出來。問題不在你寫了幾個函式,而在它們被綁成一個無法中斷的大塊。知道病根在哪,下一步就是先找出真正卡住的地方,再談怎麼拆。
診斷第一步:從現場資料找出真正慢在哪一頁、哪個互動
優化的第一步不是改程式碼,是先找對目標。你需要的是真實環境的現場資料,因為INP是整趟造訪累積出來的分數,實驗室裡跑一次未必重現得了使用者實際遇到的卡頓。
PageSpeed Insights 看 CrUX 現場數據
如果網站流量夠、符合Chrome使用者體驗報告的收錄資格,直接打開PageSpeed Insights就能看到來源層級、甚至部分情況下到網址層級的INP現場數值,這是成本最低的起點。不過CrUX只會告訴你「這裡有問題」,不會告訴你是哪個互動、哪段程式碼造成的,這個缺口要靠接下來兩個工具補上。
用 web-vitals 函式庫的 onINP 記錄實際互動
想要更細的情境資料,可以導入Google建構的web-vitals JavaScript函式庫,用onINP記錄每一次互動的延遲、發生在哪個元素、屬於三段延遲裡的哪一段。這個做法特別適合流量還沒大到符合CrUX收錄資格的網站,沒有現場資料可看時,自己蒐集一份。
import { onINP } from 'web-vitals';
onINP(console.log);Code language: JavaScript (javascript)
這段程式碼會在每次INP值需要回報時,把完整的互動資訊印出來,你可以再依需求接上自家的分析系統。
用 Long Animation Frames API 揪出拖慢畫面的腳本
另一個比較新、也還沒被廣泛寫進教學文章的工具,是Long Animation Frames(LoAF)API。它能回報是哪些腳本、來自哪個網域,實際造成了算繪延遲,比單純看見「這裡有個Long Task」更進一步,直接指到腳本檔名或函式層級,特別適合用來揪出呈現延遲被什麼卡住。
performance.getEntriesByType('long-animation-frame');Code language: JavaScript (javascript)
有了它,你不用再靠猜的去縮小範圍,而是直接鎖定拖慢畫面的那幾支腳本。
診斷第二步:在 DevTools 裡重現慢互動、抓出真兇
鎖定問題頁之後,打開Chrome DevTools的效能面板,照著使用者的操作流程把那個慢互動錄下來。兩個小技巧能讓結果更貼近真實:用訪客設定檔開瀏覽器再錄製,可以排掉外掛擴充功能造成的雜訊;切到行動裝置模擬並開啟CPU節流,則能貼近多數使用者實際裝置的效能水準,而不是你手上那台高規格的開發機。
錄製時機也有講究,重點是在頁面剛載入完成、主執行緒最忙的時候去操作,那是最容易抓到問題的時機。錄完之後,在時間軸上找那些被標成Long Task的區塊,看它對應到哪一段函式。你要確認的是,這次互動的延遲主要卡在輸入延遲、處理時間,還是呈現延遲,卡在哪一段,下一步就往哪一段下手,而不是三段一起亂槍打鳥。
修輸入延遲:把長工作拆開,讓主執行緒喘口氣
抓出兇手之後,先處理最前面的輸入延遲。核心思路很簡單,把不急的工作往後讓,騰出主執行緒優先回應使用者。
setTimeout 分流法,以及它排隊排到後面的問題
最傳統的做法,是把不急的工作丟進setTimeout,讓瀏覽器先有機會處理其他事。以前面「送出訂單」的例子來說,檢查庫存、扣款、更新畫面這幾件使用者馬上要看到回饋的事留在前面先做;寫入訂單資料庫、送出分析事件這兩件使用者當下看不出差別的事,就可以延後。
function submitOrder() {
checkStock();
chargePayment();
updateUI();
setTimeout(() => {
saveOrderToDB();
sendAnalytics();
}, 0);
}Code language: JavaScript (javascript)
不過setTimeout有個老問題,讓出去之後,續做的工作會被排到佇列的最後面。萬一這時候別的腳本插了一堆工作進來,你的後半段就得排在它們後面,可能拖很久才輪到。
scheduler.yield():讓出主執行緒,續作業還能插隊
這裡推薦用scheduler.yield()。它是專門為這件事設計的API,你在async函式裡await scheduler.yield(),函式就會在那一刻暫停、把主執行緒讓出去,等瀏覽器處理完手邊的高優先工作,再回來執行剩下的部分。
async function submitOrder() {
checkStock();
chargePayment();
updateUI();
await scheduler.yield();
saveOrderToDB();
sendAnalytics();
}Code language: JavaScript (javascript)
它最大的好處,是讓出去之後續做的工作會被優先排回來執行,排在其他同類新工作的前面。這正是它贏過setTimeout的地方。setTimeout把續做的工作排到佇列最後面,scheduler.yield()則優先插回前面,你主動讓步,也不會反而被別人的腳本插隊卡住。

批次讓步,50毫秒當門檻,別切過頭
切工作不是切越碎越好。scheduler.yield()雖然快,但每讓一次、回來一次都有一點額外開銷。如果在一個迴圈裡每跑一小步就讓一次,光是讓步和恢復的成本,可能比真正在做的事還多,反而更慢。
聰明的做法是批次處理,只有在距離上次讓步已經超過一段時間時才讓一次。常用的門檻就是50毫秒,盡量讓工作逼近、但不越過Long Task的紅線。實作上,記住上次讓步的時間點,每跑完一小段就比對一下,超過50毫秒才讓步並重設計時。你也可以依需求微調這個門檻,在回應速度和整批跑完的總時間之間取個平衡。
瀏覽器不支援 scheduler.yield() 時的備援寫法
目前scheduler.yield()主要是Chromium系瀏覽器支援,其他瀏覽器需要備援方案。常見做法是先做功能偵測,支援就用原生API,不支援就退回用Promise包一層setTimeout來讓步,雖然拿不到「優先續做」的好處,但至少能讓瀏覽器保持回應。也可以直接引入scheduler-polyfill,讓不同瀏覽器的行為盡量一致。
function yieldToMain() {
if (globalThis.scheduler?.yield) {
return scheduler.yield();
}
return new Promise((resolve) => setTimeout(resolve, 0));
}Code language: JavaScript (javascript)
修處理時間:讓事件回呼少做事,重工作丟出主執行緒
對應處理時間這段,核心思路是回呼裡能少做的就少做。以下三個常見做法,分別從版面配置、運算負擔、事件觸發頻率下手。
避免強制同步版面配置,別讓讀寫版面資料黏在一起
版面配置抖動,也就是強制同步版面配置,是一種常見的算繪效能陷阱。當你在JavaScript裡先改了樣式,緊接著在同一段工作裡又去讀取版面資料,例如某些會觸發版面重算的屬性,瀏覽器就被迫立刻插入一次同步版面計算,而不是等事件回呼跑完後再一次處理,處理時間因此被拖長。
正確做法是把「讀」跟「寫」分開批次處理,所有要讀版面資料的動作集中在前面一次做完,所有要改動樣式的動作集中在後面一次做完,別讓兩者交錯黏在一起。
把耗時運算丟給 Web Worker,主執行緒只管畫面
如果某段運算本身很重、但跟畫面更新關係不大,例如大量資料的排序、篩選、格式轉換,可以整段搬到Web Worker上跑,因為它完全不占用主執行緒。主執行緒只負責把資料丟給Worker、收回算完的結果、更新畫面。
這跟scheduler.yield()是兩種不同層級的手法。scheduler.yield()仍然排在主執行緒裡,只是排隊順序被優先安排;Web Worker則是直接把工作搬出主執行緒。運算真的很重、又跟DOM操作無關時,搬到Worker上跑往往比一直讓步更根本。
高頻互動先節流防抖,第三方腳本要盯緊
捲動、輸入框打字這類高頻互動,如果每次事件都觸發一次重運算,處理時間很容易一口氣拉長。用防抖或節流降低回呼觸發的頻率,是最基本、也最容易被忽略的一招。
另一個常被忽略的隱形殺手,是外掛或外部嵌入的第三方腳本:追蹤碼、線上客服、社群留言板、廣告腳本,這些東西常常在背景排一堆工作搶主執行緒。能延後載入的延後、能拿掉的拿掉,盤點一次網站上到底掛了幾支這樣的腳本,往往比改自家程式碼更快看到效果。
修呈現延遲:縮小 DOM、讓看不到的內容延後算
對應呈現延遲這段,兩個常見手法是縮小DOM規模,以及延後算繪畫面外的內容。
DOM越大,通常算繪要花的力氣就越多。雖然算繪成本跟DOM大小不是單純的線性關係,但大型DOM要處理的初始算繪與互動後的重新算繪,確實都比小型DOM複雜。畫面外的元素,可以用CSS的content-visibility屬性延後算繪,等它接近可視範圍再處理,把力氣留給看得到的部分。
如果網站是單頁應用,大量用JavaScript在用戶端算繪HTML時也要留意成本。瀏覽器不僅要花時間執行JavaScript產生那些HTML,在算繪完成之前,畫面也不會有任何內容。能讓伺服器先吐出基本內容、再用JavaScript補強互動,通常會是比較穩的做法。
兩個真實案例:INP 修好後,訂票網站多賣 7%、房產平台轉換多三成六
抽象的招式講再多,不如看兩個真的把數字修下來的案例。
redBus:拆解排隊延遲+改本地狀態同步,INP 大砍、業績多 7%
印度巴士訂票網站redBus,在INP還被視為實驗指標的時期,就針對它做過一輪認真優化。他們的搜尋結果頁原本有兩個明顯卡點。
第一個卡點出在延遲載入。使用者往下捲動時,scroll事件監聽器本身就排程了大量必須在主執行緒上跑的工作,系統還會去API抓更多票價,而且每次一抓就是一大批,回應又重又慢。redBus對scroll事件處理常式做了去抖動,降低回呼觸發的頻率;同時用requestAnimationFrame幫算繪工作排優先序;再把每次抓取的票價筆數從30筆降到10筆。這幾項改動加總,讓這個頁面的INP從870到900毫秒的區間,降到350到370毫秒,單這一步就把延遲砍掉超過一半。
第二個卡點在輸入框。原本使用者在搜尋頁改日期、改條件,每次輸入的change事件都會呼叫一個很重的函式,透過Redux更新整個介面,造成大量不必要的重繪。redBus改成把輸入狀態先存在本地,只在離開輸入框時才同步到全域狀態,重繪頻率因此大幅降低,這一項變更讓該頁的INP又再進步72%。
把這些優化加總起來,redBus的整體銷售額成長了約7%。這個案例點出兩件事:INP不是抽象的工程指標,它直接連到使用者願不願意走完流程、最後掏不掏錢;真正有效的優化,往往不是砍掉某個功能,而是換個方式排程同樣的工作。

QuintoAndar:找出 4 秒延遲的搜尋頁,INP 降 80%、轉換多三成六
巴西最大的住房平台QuintoAndar,則示範了另一個常被忽略的重點:效能問題不只靠工程手法解決,還得靠組織治理。2024年初,QuintoAndar發現自己雖然坐擁市場上數一數二的平台,INP表現卻是同業裡最差的一批。他們發起一個內部代號「黃色警報」的機制,概念借自Google,一旦宣布,指定主管就能徵召公司內任何團隊協助,不論那些人手上原本在忙什麼專案。
透過真實使用者監控搭配Long Animation Frames分析,QuintoAndar找到房源搜尋體驗裡,有一段互動的第75百分位延遲高達4秒,影響了25%的使用者。當時瀏覽器對原生scheduler.yield()的支援還不夠廣,他們自己寫了一個用setTimeout包裝的yieldToMain()函式做同樣的讓步效果,搭配React的useTransition讓狀態更新不擋住畫面,也對輸入做防抖,並拿掉了會拖慢主執行緒的第三方追蹤像素與CSS-in-JS。
比技術修法更值得學的,是他們的長期治理機制:建立固定與浮動雙門檻的告警系統,寫成標準作業手冊,新版本用Canary機制分階段釋出,從1%、10%、65%到100%,只要哪一階段偵測到效能倒退,就自動回復到前一版。最終,QuintoAndar的INP整體降低80%,不良頁面比例從32%降到6.9%,轉換量年增36%。

INP最容易讓人挫折的地方,在於它不像圖片壓縮那樣改一次就永久有效。它是整頁、整趟使用者旅程累積出來的分數,網站每加一個功能、每接一個第三方腳本,都可能讓某個互動重新變慢。與其把它當成一次要清空的待辦,不如像QuintoAndar那樣,把「找出慢互動、診斷卡在哪一段、對症修好、驗證效果、留意有沒有倒退」變成日常會做的事,而不是修一次就丟著不管。
真正撐住INP分數的,從來不是某個神奇的API,而是願不願意持續回頭看使用者真實遇到了什麼。把這件事做成習慣,分數自然會慢慢往綠色靠。
