• 精確說出每個指標量的是什麼、從哪裡量到哪裡、門檻是多少。

  • 知道哪些指標已經被淘汰(FMP、FID)、被什麼取代、以及為什麼。

  • 分辨 Core Web Vitals 與「診斷用指標」,並知道哪些只能在真實使用者環境量測。

  • 能在自己的頁面上把這些指標印出來。

把指標放到時間線上

圖 1.1 將本章所有指標放在同一條頁面生命週期時間線上。左半部(TTFB、FP、FCP、LCP)是「載入」階段的時間點;INP 是整段互動期間所有互動延遲的彙整;CLS 則是整個生命週期中版面跳動的累計。

max width=

各指標在頁面生命週期中的位置

TTFB:Time to First Byte

從導覽開始(使用者按下 Enter 或點擊連結)到收到 HTML 回應的第一個位元組的時間。依 web.dev 的定義,它包含:重導時間、Service Worker 啟動時間、DNS 查詢、TCP 與 TLS 連線建立、送出請求到首位元組抵達之間的等待(伺服器處理時間)。

  • 門檻: \(\leq 800\) ms; 800–1800 ms; \(> 1800\) ms(以 p75 計)。

  • 性質:不是 Core Web Vitals,而是最重要的診斷指標。TTFB 是 FCP 與 LCP 的下限:HTML 還沒到,什麼都畫不出來。

  • 對 SPA 尤其關鍵:純客戶端渲染的頁面拿到 HTML 後還要下載 JS、執行、抓資料才有內容,所以 TTFB 每慢 1 ms,後面全部順延。反過來,伺服器端渲染的頁面 TTFB 可能較高(伺服器要組 HTML),但 FCP / LCP 反而更早——這就是為什麼 web.dev 說 TTFB 門檻是「粗略指引」,要和你的架構一起看。

  • 量測:所有主流瀏覽器都支援,取 ()。已把 TTFB 拆解到 RTT 層級。

FP:First Paint

瀏覽器第一次在畫面上渲染任何東西的時間,排除預設的白色背景,但包含非預設的背景色與 iframe 的外框。由 Paint Timing API 以 這個 entry 提供。

FP 回答的是「有在動嗎?」的最低門檻:使用者看到畫面從空白變成了「有東西」。規範將回報 FP 列為選擇性(Safari 就不回報 FP,只回報 FCP),且它沒有官方的良好 / 不佳門檻,主要用於診斷。在大多數頁面上 FP 與 FCP 幾乎同時,若兩者差距大,通常表示頁面先畫了一個有色背景或骨架,但真正的內容還在等(例如等待網頁字型或 JS)。

FCP:First Contentful Paint

從導覽開始到瀏覽器第一次渲染出任何 DOM 內容的時間。「內容」包括:文字(含仍在等待網頁字型的文字不算,需實際畫出)、、、非白色的 、CSS 背景圖片。iframe 內的內容不計入。

  • 門檻: \(\leq 1.8\) s; 1.8–3.0 s; \(> 3.0\) s。

  • 性質:不是 Core Web Vitals,但是 Lighthouse 效能分數的組成之一(權重 10%),也是 CrUX 中回報的指標。

  • 常見瓶頸:TTFB 高; 內的 render-blocking CSS 與同步 JS;網頁字型以 (FOIT)方式載入導致文字遲遲不畫;SPA 要等 JS 執行才有內容。

  • 量測:Paint Timing API 的 entry;Chromium、Firefox、Safari 都支援。

FMP:First Meaningful Paint(已淘汰)

Chrome 早期提出的啟發式指標,定義為「視窗上方(above-the-fold)最大的版面變化發生之後、且網頁字型已載入」的那次繪製,試圖回答「有用了嗎?」。

FMP 的問題在於它依賴 Chrome 渲染引擎的內部實作(統計 layout object 的數量變化),對頁面載入的微小差異極度敏感,常出現雙峰(bimodal)分布,且無法標準化、其他瀏覽器無法實作。Lighthouse 6.0(2020 年 5 月)將它棄用,並由 接替「主要內容何時出現」這個問題;後續版本已完全移除。

許多 2020 年前後撰寫的效能書籍仍花不少篇幅介紹 FMP、TTI 與 FID。閱讀時請自動把「FMP」對應到今天的「LCP」、「FID」對應到「INP」,並記得 TTI 也已在 Lighthouse 10 移除。這些指標背後的「問題」沒有變,變的是回答問題的量測方法。

LCP:Largest Contentful Paint

從導覽開始到視窗內最大的圖片、文字區塊或影片完成渲染的時間。它是「主要內容是否已呈現」的代理指標,屬於 Core Web Vitals。

哪些元素會被視為 LCP 候選?

依 Largest Contentful Paint API 規範與 web.dev 的整理:

  • 元素(動畫圖片如 GIF 以第一幀呈現時間計)。

  • 內的 元素。

  • 元素:取 poster 圖片載入時間或影片第一幀呈現時間,較早者為準。

  • 以 CSS 載入背景圖的元素(CSS 漸層不算)。

  • 包含文字節點或行內文字子元素的區塊級(block-level)元素。

排除規則(Chromium)

  • 的元素(使用者看不到)。

  • 覆蓋整個視窗的元素(通常是背景而非內容)。

  • 低資訊熵的圖片:自 Chrome 112 起,圖片的「編碼位元數 ÷ 顯示像素數」低於 0.05 bits/pixel 者不列入,用來排除純色佔位圖、極度模糊的 LQIP 與大面積漸層圖,避免有人用超小的假圖「作弊」。

  • 文字元素只計算包住所有文字節點的最小矩形;所有元素都不計 margin、padding、border。

  • 元素的大小以視窗內可見部分計;超出視窗或被裁切的部分不算。圖片若被放大顯示,以原始尺寸與顯示尺寸的較小者計。

LCP 如何被「決定」?

瀏覽器在載入過程中會持續回報新的、更大的候選元素(每個都是一筆 entry);最後一筆就是 LCP。一旦使用者與頁面互動(點擊、觸控、按鍵、捲動),瀏覽器就停止回報,因為互動之後的內容變化不再算「載入」。因此在 RUM 中要在頁面隱藏()或互動時才送出最終值。

  • 門檻: \(\leq 2.5\) s; 2.5–4.0 s; \(> 4.0\) s。

  • 支援:Chromium 系瀏覽器;Firefox 自 122 版起支援 LCP API;Safari 不支援。

  • 會把 LCP 拆成四個子部分(TTFB、資源載入延遲、資源載入時間、元素渲染延遲)逐一優化。

FID:First Input Delay(已由 INP 取代)

使用者第一次與頁面進行離散互動(點擊、觸控、按鍵;不含捲動與縮放)時,從輸入發生到瀏覽器開始執行事件處理器之間的延遲。它只量「輸入延遲」,不含事件處理時間,也不含後續渲染。

  • 門檻(歷史): \(\leq 100\) ms; \(> 300\) ms。

  • 為什麼被取代:(1) 只看第一次互動,但使用者在頁面上的大部分互動發生在載入完成後;(2) 只量延遲那一小段,事件處理器本身跑了 500 ms、畫面更新又拖了 200 ms 都不算;(3) 到 2023 年超過九成網站都「通過」FID,指標已失去鑑別力。

  • Google 於 2024 年 3 月 12 日正式以 INP 取代 FID 成為 Core Web Vitals; 套件 v5 已移除 。

INP:Interaction to Next Paint

觀察頁面整個生命週期中所有點擊、觸控與鍵盤互動的延遲,回報其中接近最差的一個值(互動次數少於 50 次時取最大值;之後每 50 次互動忽略一個最高值,約等於第 98 百分位)。單次互動的延遲是從使用者輸入開始,到瀏覽器畫出下一個包含互動結果的畫格為止。

什麼算一次「互動」?

一次互動是由同一個使用者動作觸發的一組事件:

  • 滑鼠點擊: \(\rightarrow\) \(\rightarrow\) 。

  • 觸控: \(\rightarrow\) (以及可能的 )。

  • 鍵盤: \(\rightarrow\) (輸入文字時每個按鍵都是一次互動)。

同一次互動裡多個事件的延遲取最長者,不是相加。hover 與捲動不算互動(捲動由瀏覽器的合成執行緒處理,通常不受主執行緒阻塞影響)。

三個子部分

每次互動的延遲 = 輸入延遲(input delay,事件發生到處理器開始執行)+ 處理時間(processing duration,所有事件處理器執行完畢)+ 呈現延遲(presentation delay,處理完到下一個畫格上屏)。會針對這三段分別討論優化。

  • 門檻: \(\leq 200\) ms; 200–500 ms; \(> 500\) ms。

  • 性質:Core Web Vitals;只能在真實使用者環境量測(實驗室沒有真人互動)。實驗室以 TBT 作為代理,DevTools 的 Performance 面板則可在你手動互動時即時顯示每次互動的延遲。

  • 支援:以 Event Timing API 為基礎,目前為 Chromium 專有。

  • 目前 INP 是三個 Core Web Vitals 中最多網站無法通過的一項,主因是龐大的 JavaScript、第三方腳本與 hydration 造成的長任務。

CLS:Cumulative Layout Shift

衡量頁面生命週期中非預期版面跳動的嚴重程度。每次跳動有一個「版面偏移分數」(layout shift score);CLS 取最大的一段「跳動叢集」(session window)的分數總和。

單次版面偏移分數

\[\text{layout shift score} = \text{impact fraction} \times \text{distance fraction}\]

  • 影響比例(impact fraction):前後兩個畫格中,所有「不穩定元素」佔據區域的聯集面積 ÷ 視窗面積。例如一個佔視窗 50% 高度的元素往下移了 25% 視窗高度,前後兩個位置的聯集是 75% \(\Rightarrow\) 0.75。

  • 距離比例(distance fraction):不穩定元素在該畫格中移動的最大距離(水平或垂直)÷ 視窗的較長邊。上例為 0.25。

  • 該次分數 \(= 0.75 \times 0.25 = 0.1875\)。

Session window 與排除規則

  • 連續的跳動若彼此間隔少於 1 秒,會被歸入同一個 session window;單一 window 最長 5 秒。CLS = 所有 window 中分數總和最大者。這是 2021 年的修訂,目的是不讓長時間開著的頁面(如無限捲動)分數無限累積。

  • 使用者輸入後 500 ms 內的跳動被視為「預期中的」(),不計入 CLS。但注意:只有離散輸入(點擊、觸控、按鍵)算;捲動、拖曳、縮放不算最近輸入。

  • 只有起始位置改變才算跳動;新元素出現在原本空白處、或元素只改變尺寸但起點不動,都不算。以 做的動畫也不算(它不影響版面)。

  • 門檻: \(\leq 0.1\); 0.1–0.25; \(> 0.25\)(無單位)。

  • 性質:Core Web Vitals;實驗室與真實使用者皆可量,但實驗室通常只量到載入階段,真實使用者才會量到之後的互動與延遲載入內容。

  • 支援:Layout Instability API,Chromium 專有。

補充:Lighthouse 中的其他實驗室指標

L3.2cm L7.4cm L3.6cm 指標 & 定義 & 狀態
Speed Index(SI) & 以逐幀截圖分析視覺內容「填滿」的速度,越早填滿越低。 & Lighthouse 分數組成,權重 10%。
Total Blocking Time(TBT) & FCP 到 TTI 之間,所有長任務(\(>\)50 ms)超過 50 ms 部分的總和。 & Lighthouse 分數組成,權重 30%;INP 的實驗室代理。
Time to Interactive(TTI) & 頁面「可靠地」可互動的時間點(FCP 後主執行緒安靜 5 秒)。 & Lighthouse 10 已移除,因與使用者感受相關性低。
Max Potential FID & 最長任務的長度,作為 FID 上限估計。 & Lighthouse 6.0 已移除。

Lighthouse 10 之後的效能分數權重為:FCP 10%、SI 10%、LCP 25%、TBT 30%、CLS 25%。請記得這是實驗室分數,與 CrUX 的真實使用者評分是兩套系統。

總整理

L1.3cm L3.8cm C1.6cm C1.6cm C1.4cm C1.5cm L2.7cm 指標 & 量什麼 & 良好 & 不佳 & CWV? & Lab / Field & 來源 API
TTFB & 導覽開始到 HTML 首位元組 & \(\leq 0.8\) s & \(> 1.8\) s & 否(診斷) & 兩者 & Navigation Timing
FP & 第一次畫出任何非預設像素 & – & – & 否 & 兩者 & Paint Timing
FCP & 第一次畫出 DOM 內容 & \(\leq 1.8\) s & \(> 3.0\) s & 否 & 兩者 & Paint Timing
FMP & 最大版面變化後的繪製 & – & – & 已淘汰 & Lab & Chrome 內部啟發式
LCP & 視窗內最大內容元素完成渲染 & \(\leq 2.5\) s & \(> 4.0\) s & 是 & 兩者 & LCP API
FID & 第一次互動的輸入延遲 & \(\leq 100\) ms & \(> 300\) ms & 已被 INP 取代 & Field & Event Timing
INP & 所有互動延遲的近最差值 & \(\leq 200\) ms & \(> 500\) ms & 是 & Field & Event Timing
CLS & 最大跳動叢集的分數總和 & \(\leq 0.1\) & \(> 0.25\) & 是 & 兩者 & Layout Instability
TBT & 長任務阻塞時間總和 & \(< 200\) ms & \(> 600\) ms & 否 & Lab & Long Tasks

web.dev 說明 Core Web Vitals 門檻的訂定原則有兩個:(1) 符合人類感知研究——例如 100 ms 內的回饋會被感知為「即時」,1 秒內的變化不會打斷思路;(2) 可達成性——「良好」門檻必須是當時約前 10% 的網站已能達到的水準,「不佳」門檻則對應大約後 30% 的網站。這些門檻自 2020 年公布、2024 年 INP 加入後至今(2026 年)未再調整。

課堂實作

不使用任何套件,直接用 PerformanceObserver(會詳細解釋):

// TTFB
const nav = performance.getEntriesByType('navigation')[0];
console.log('TTFB', Math.round(nav.responseStart), 'ms');

// FP / FCP
new PerformanceObserver((list) => {
  for (const e of list.getEntries()) console.log(e.name, Math.round(e.startTime), 'ms');
}).observe({ type: 'paint', buffered: true });

// LCP:持續回報候選,最後一筆才是最終值
new PerformanceObserver((list) => {
  const last = list.getEntries().at(-1);
  console.log('LCP 候選', Math.round(last.startTime), 'ms', last.element, last.url);
}).observe({ type: 'largest-contentful-paint', buffered: true });

// CLS:只累加沒有最近輸入的跳動(此處為簡化版,未實作 session window)
let cls = 0;
new PerformanceObserver((list) => {
  for (const e of list.getEntries()) if (!e.hadRecentInput) cls += e.value;
  console.log('CLS 累計', cls.toFixed(4));
}).observe({ type: 'layout-shift', buffered: true });
  1. 開啟 PageSpeed Insights,輸入一個流量足夠的網站(例如新聞或電商網站)。

  2. 上半部「探索實際使用者的體驗」是 CrUX 的 28 天 p75 數據,注意 LCP、INP、CLS 三項是否都為綠色。

  3. 下半部是 Lighthouse 實驗室分數。找出兩者不一致的地方(例如實驗室 LCP 很好但真實 LCP 不佳),討論可能原因:真實使用者的裝置與網路分布?快取狀態?地理位置?

  4. 切換「行動裝置 / 電腦」,觀察差異。Google 排名主要看行動版數據。

  1. 一個頁面的 TTFB 是 300 ms、FCP 是 2.9 s,中間差了 2.6 s。請列出至少三種可能造成這段差距的原因。

  2. 某電商首頁的 LCP 元素在 Chrome 中被判定為一個 40 KB 的橫幅圖片,但設計師把它換成一張 3 KB 的極度模糊漸層圖後 LCP 從 3.1 s 變成 1.2 s。這算改善嗎?Chrome 112 之後會怎麼處理?

  3. 為什麼 INP 要「每 50 次互動忽略一個最高值」而不是直接取最大值?

  4. 一個使用無限捲動的社群動態頁面,在 CLS 改為 session window 計算方式之前與之後,分數會有什麼不同?為什麼舊算法對這類頁面不公平?

  5. 為什麼 FID 幾乎每個網站都「通過」,卻仍有大量網站的 INP 不佳?兩者定義上的哪個差異造成這個現象?

  • 載入階段的時間點指標依序為 TTFB \(\rightarrow\) FP \(\rightarrow\) FCP \(\rightarrow\) LCP,每一個都以前一個為下限。

  • Core Web Vitals 目前為 LCP(\(\leq\)2.5 s)、INP(\(\leq\)200 ms)、CLS(\(\leq\)0.1),以真實使用者 28 天 p75 評分,三項全達良好才算通過。

  • FMP 與 FID 已淘汰;讀舊書時請對應到 LCP 與 INP。TTI 也已從 Lighthouse 移除。

  • INP 與 FID 只能在真實使用者環境量測;TBT 是實驗室代理。LCP、CLS、INP 的 API 主要是 Chromium 專有,TTFB 與 FCP 則跨瀏覽器可用。

  • CLS 的公式是 impact fraction \(\times\) distance fraction,並以 1 秒間隔、5 秒上限的 session window 取最大叢集。