怎麼量 Core Web Vitals?實驗室數據與真實使用者數據
同一個 核心指標 有兩種來源,用途完全不同。搞混它們是效能優化最常見的挫折來源,「我明明改好了,數據怎麼沒動」。
兩種數據的分工
| 實驗室數據 | 真實使用者數據 | |
|---|---|---|
| 英文 | Lab Data | Field Data / RUM |
| 來源 | 在固定條件下模擬一次載入 | 真實造訪的實際紀錄 |
| 工具 | Lighthouse 、DevTools | CrUX、Search Console 、自埋量測 |
| 反映速度 | 立刻 | 有延遲 |
| 涵蓋範圍 | 一次載入 | 所有裝置與網路 |
| 取值方式 | 那一次的結果 | 第 75 百分位 |
| 用途 | 除錯 | 驗收與評分 |
一句話記住
用實驗室數據找出是哪個資源的問題,用真實數據確認真的改善了。
為什麼兩邊數字會差很多
這不是哪一邊錯了
| 差異來源 | 說明 |
|---|---|
| 裝置 | 您的電腦比多數使用者的手機快得多 |
| 網路 | 本機或機房的網路比行動網路快 |
| 快取狀態 | 實驗室通常是全新造訪,真實使用者可能有快取 |
| 使用者行為 | INP 需要真的有人互動才量得到 |
| 取值方式 | 實驗室是單次,真實數據取第 75 百分位 |
所以 Lighthouse 拿到 95 分而 Search Console 顯示「不佳」是完全正常的,後者才是評分依據。
CrUX 與 28 天觀察窗
Chrome 使用者體驗報告(CrUX)是官方真實數據的來源。它有兩個特性要知道:
28 天滾動觀察窗
CrUX 的數據是 過去 28 天 的彙總。所以:
- 改善上線後,數據要花將近一個月才完整反映。
- 中間的過渡期會看到新舊資料混在一起,數字慢慢移動。
- 不要因為改完當天數據沒動就以為沒效。
流量太少查不到數據
CrUX 需要足夠的造訪量才會提供某個網址的數據。小網站常遇到「查不到這個頁面」的情況。
退路有兩個:看同網域的整體數據(origin-level),或 自己埋設量測。
查真實數據的三個地方
| 工具 | 粒度 | 適合 |
|---|---|---|
| Search Console 的核心指標報告 | 網址群組 | 找出有問題的頁面類型 |
| PageSpeed Insights | 單一網址 | 同時看實驗室與真實數據 |
| CrUX 的公開資料集 | 網域或網址 | 自己做趨勢分析 |
Search Console 會把相似的頁面歸成群組
它不會逐一列出每個網址,而是把版型相似的頁面歸成一組(例如:所有商品頁)。這其實很有用,問題通常是版型層面的,修一個模板就修好一整組。
自己埋設量測
官方數據有延遲、以群組彙總,也不會告訴您 是哪個元素 造成問題。自己埋設可以補上這三點。
用 web-vitals 套件
npm install web-vitalsimport { onLCP, onINP, onCLS } from 'web-vitals';
function send(metric) {
// 用 sendBeacon 確保頁面關閉時也能送出
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating, // good / needs-improvement / poor
path: location.pathname,
// 造成問題的元素,這是官方數據給不了的
target: metric.attribution?.element ?? null,
});
navigator.sendBeacon?.('/api/vitals', body) ??
fetch('/api/vitals', { body, method: 'POST', keepalive: true });
}
onLCP(send);
onINP(send);
onCLS(send);用 attribution 版本拿到具體元素
import { onLCP, onINP, onCLS } from 'web-vitals/attribution';這個進階版會在 metric.attribution 裡附上:LCP 是哪個元素、INP 是哪次互動、CLS 是哪些元素在偏移,這是排查時最有價值的資訊。
不裝套件的做法
三個指標都可以用 PerformanceObserver 自己觀察:
// LCP:取使用者互動前的最後一筆
new PerformanceObserver((list) => {
const entries = list.getEntries();
const last = entries[entries.length - 1];
console.log('LCP:', last.startTime, last.element);
}).observe({ type: 'largest-contentful-paint', buffered: true });
// CLS:累加非使用者操作造成的位移
let cls = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
cls += entry.value;
console.log(
'累積 CLS:',
cls,
entry.sources?.map((s) => s.node),
);
}
}
}).observe({ type: 'layout-shift', buffered: true });
// 互動延遲:記錄超過門檻的互動
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.interactionId) {
console.log('互動延遲:', entry.duration, entry.name, entry.target);
}
}
}).observe({ type: 'event', buffered: true, durationThreshold: 40 });自己算 CLS 與 INP 有細節要處理
CLS 的正式定義是「工作階段窗口」的最大值,不是單純累加;INP 是所有互動中接近最差的那一次,而非最後一次。
要精確的話用 web-vitals 套件,它已經處理好這些規則。上面的程式碼適合快速除錯,不適合當正式的量測。
送去哪裡
| 目的地 | 適合 |
|---|---|
| 自家後端 API | 要完全掌控資料、做自訂分析 |
| 分析工具的自訂事件 | 已經在用分析工具 |
| 專門的 RUM 服務 | 不想自己維護儲存與儀表板 |
用 sendBeacon 而不是 fetch 的理由:頁面關閉時 fetch 可能被中斷,sendBeacon 會由瀏覽器在背景送完。
本機除錯的正確設定
本機不節流測不出真實情況
您的電腦處理器太快,一個在中階手機上 300 毫秒的長任務,在本機可能只有 30 毫秒。INP 的問題在本機幾乎測不出來。
開 DevTools 之後設定這兩項:
| 設定 | 位置 | 建議值 |
|---|---|---|
| CPU 節流 | Performance 面板的齒輪 | 4x 或 6x slowdown |
| 網路節流 | Network 面板的 Throttling | Slow 4G |
再搭配 Rendering 面板的這幾個選項:
| 選項 | 用途 |
|---|---|
| Layout Shift Regions | 位移的區域會閃藍色,找 CLS 最快 |
| Paint flashing | 重繪的區域會閃綠色 |
| Frame Rendering Stats | 即時的幀率與 GPU 記憶體 |
兩邊不一致時怎麼判讀
| 症狀 | 通常的原因 | 怎麼做 |
|---|---|---|
| 實驗室好、真實差 | 本機裝置與網路太好 | 開節流重測,並看真實數據細分哪種裝置差 |
| 實驗室差、真實好 | 多數使用者有快取,或測到了異常的一次 | 多跑幾次取趨勢 |
| 改完真實數據沒動 | 28 天觀察窗還沒反映 | 等,或看自埋量測的即時數據 |
| 只有行動裝置差 | 中低階手機的 CPU 與網路 | 開 CPU 節流專門處理 INP |
| 分數每次都不同 | 實驗室數據本來就有浮動 | 看多次結果的中位數,不要用單次下結論 |
建立一個可持續的流程
不要只在「感覺變慢」時才量
| 時機 | 做什麼 |
|---|---|
| 每次提交程式碼 | Lighthouse CI 設分數門檻擋退步 |
| 每週 | 看自埋量測的趨勢 |
| 每月 | 看 Search Console 的核心指標報告 |
| 改版前後 | 記下基準值,改版後比對 |
自埋量測的資料建議至少存這幾個維度,才能細分問題:
{
name: 'LCP',
value: 2840,
rating: 'needs-improvement',
path: '/seo/core-web-vitals',
target: 'img.banner', // 哪個元素
deviceType: 'mobile', // 裝置類型
connection: '4g', // 網路類型
timestamp: 1757000000000,
}這個站的做法
這個站目前 沒有埋設 RUM,只在正式機載入 GA4:
// 僅正式機設定 VITE_GA_ID,留空則不載入
...(gaId
? [
['script', { async: '', src: `https://www.googletagmanager.com/gtag/js?id=${gaId}` }],
['script', {}, `window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gaId}');`],
]
: []),靜態站的效能問題比較單純
沒有 SSR、沒有網頁字型、橫幅是幾 KB 的 SVG,這種組合的核心指標通常不會有問題。真實數據看 Search Console 就夠了,不必額外埋設。
要不要自埋量測,取決於您有沒有需要細分的問題。 有網頁字型、有廣告、有大量互動的網站才真的需要。
檢查清單
| 項目 | 標準 |
|---|---|
| 知道實驗室與真實數據的分工 | 必備 |
| 用真實數據做評分依據 | 必備 |
| 本機除錯時開了 CPU 與網路節流 | 建議 |
| 改完之後有等真實數據反映 | 必備 |
| 有在 Search Console 定期看核心指標報告 | 建議 |
| 複雜網站有埋設 RUM | 建議 |
量測用 sendBeacon 送出 | 建議 |
| CI 有設效能門檻 | 建議 |
| 不用單次分數下結論 | 必備 |
常見問題
改完之後多久會反映在數據上?
實驗室數據立刻反映,真實使用者數據要等資料累積。CrUX 採 28 天滾動觀察窗,所以完整反映改善需要將近一個月。自己埋設的量測則可以即時看到。
為什麼稽核工具的分數和搜尋主控台的數據差很多?
資料來源不同。Lighthouse 是在固定條件下模擬一次載入,Search Console 呈現的是真實使用者在各種裝置與網路下的紀錄,取第 75 百分位。前者用來除錯,後者才是評分依據。
自己埋設量測有什麼好處?
三個:即時、可以細分到單一頁面與裝置類型、還能拿到造成問題的具體元素。CrUX 有延遲、以網址群組彙總,也不會告訴您是哪個元素造成偏移。
資料量太少會怎樣?
CrUX 需要足夠的造訪量才會提供數據,流量小的網站可能查不到自己的紀錄。這種情況只能靠自己埋設量測,或退而參考同網域的整體數據。
本機測不出真實情況怎麼辦?
開開發者工具的 CPU 與網路節流,模擬中階手機與行動網路。互動延遲 的問題尤其需要這樣做,因為本機的處理器太快,長任務在本機可能只有三十毫秒。