fetchpriority="high" 讓 LCP 圖片更快載入:原理、設定與常見錯誤診斷
上個月我在一個 Next.js 電商專案上加了一行 fetchpriority="high",Lighthouse 的 LCP 從 3.8s 掉到 2.1s,WebPageTest 的瀑布圖裡那個圖片請求從 Low 跳到 High,整個時序一目了然。這個屬性的原理是什麼?為什麼一行 HTML 屬性可以讓 LCP 優化跳一個量級?以及更重要的問題:為什麼有人加了這個屬性,LCP 還是沒動?這篇記錄我把這個問題拆開來看的過程。 (如果 LCP 元素是文字而非圖片,可參考 font-display 對文字 LCP 的影響)
fetchpriority 屬性是什麼?
fetchpriority 是一個 HTML 屬性,用來告訴瀏覽器某個資源的載入優先順序比默認值更高或更低,目前已是 W3C 標準的一部分(2023 年 2 月標準化)。它屬於 Fetch Priority API 的 HTML 介面,可以套用在 img、link、script、iframe 元素以及 JavaScript 的 fetch() 呼叫上。
這個屬性有三個有效值:
| 值 | 意思 | 最常用情境 |
|---|---|---|
high |
比默認優先順序更高 | LCP hero 圖片 |
low |
比默認優先順序更低 | 第三方 iframe、非關鍵腳本 |
auto |
瀏覽器自行決定(預設值) | 不需要調整的一般資源 |
注意:fetchpriority 是調整相對優先順序,不是設定絕對值。圖片的默認優先順序是 Low,加上 high 後會升到 High。但如果本來已經是 Highest 的資源(例如 render-blocking CSS),加上 high 也不會變得比 Highest 更高,只會降到 High,這一點很多人沒注意到。
為什麼 LCP 圖片的默認優先順序偏低?
瀏覽器在解析 HTML 的時候,圖片元素默認被標記為 Low 優先順序,原因是大多數圖片都在捲動範圍之外或在展開的選單裡,對首次渲染沒有直接影響。問題是 LCP 圖片是頁面最重要的視覺內容,它也被這個規則一視同仁地塞到 Low 隊列裡。
Chrome 有一個機制可以自動提升 in-viewport 圖片的優先順序,但這個提升只有在 layout 完成之後才發生。在 layout 完成之前,圖片已經在 Low 優先的隊列中排著等了。對於 LCP 圖片來說,這段等待時間直接加到 LCP 時間上。
DevTools 瀑布圖裡會看到圖片先顯示 Low,layout 之後才跳到 High,這個從 Low 到 High 之間的灰色橫條,就是 fetchpriority 能幫你省掉的時間。
Tight Mode 是什麼?fetchpriority 如何突破它
瀏覽器資源載入分兩個 phase。初始 phase 又稱 "Tight Mode":只要 in-flight 的請求少於兩個,Low 優先的資源才會被下載,否則一律等待。這個機制是為了讓 blocking 資源(CSS、關鍵 JS)優先下載完,不被大量圖片搶佔頻寬。
Tight Mode 結束的時間點是:所有 blocking scripts in the <head> 下載並執行完畢(帶 async 或 defer 的 script 不算 blocking)。這個時間點就是 DevTools 瀑布圖裡的 DOM Interactive 黃色垂直線。
當你加上 fetchpriority="high",圖片優先順序從 Low 升到 High,瀏覽器在 Tight Mode 時也會繼續下載它,不再等待。這就是為什麼加了這個屬性後,瀑布圖裡那個長長的灰色等待段消失了。
如果你的頁面有大量 render-blocking CSS 或 JS,Tight Mode 的等待時間可能很長。這也是為什麼在 Critical Rendering Path 分析中,減少 blocking 資源和加 fetchpriority 是互補的,前者縮短 Tight Mode 持續時間,後者讓 LCP 圖片不受 Tight Mode 影響。
正確加法:哪些元素支援、如何避免過度使用
把 fetchpriority="high" 加在已確認的 LCP 元素 img 標籤上,一個頁面只加一次;img、link、script、iframe 四種元素都支援這個屬性,但 LCP 圖片以外的使用情境相對少見。
<!-- 最常見:直接加在 img 元素 -->
<img src="hero.jpg" fetchpriority="high" alt="Hero image">
<!-- picture 元素需要加在內部的 img,不是 source -->
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" fetchpriority="high" alt="Hero image">
</picture>
<!-- preload link 可以加 fetchpriority -->
<link rel="preload" as="image" href="hero.jpg" fetchpriority="high">
<!-- 降低非關鍵資源:第三方 iframe -->
<iframe src="https://youtube.com/..." fetchpriority="low"></iframe>
一個頁面只在一張圖片上設 high。理由很直接:優先順序是相對的。如果你把三張圖都設成 high,三張的優先順序相對於彼此沒有差異,等同於回到默認狀態。實際上只有明確的 LCP 元素需要這個屬性,其他 above-the-fold 圖片不需要,瀏覽器 layout 後會自動提升它們。
另一個常見誤解是 fetchpriority="high" 和 loading="eager" 是同一回事。對比如下:
| 屬性 | 作用 | Tight Mode 期間 |
|---|---|---|
loading="eager" |
不延遲載入(取消 lazy loading) | 圖片仍是 Low priority,Tight Mode 中被擋住 |
fetchpriority="high" |
提升優先順序 | 圖片升為 High priority,Tight Mode 中可以下載 |
簡單說:loading="eager" 決定要不要載入,fetchpriority="high" 決定多快載入。LCP 圖片需要兩個條件同時成立:不 lazy + 高優先。圖片默認就是 eager,所以多數情況只需要加 fetchpriority="high"。
fetchpriority 與 preload 有什麼不同?
兩者都是加速資源載入的工具,但作用在不同的維度。簡單定義:preload 改變瀏覽器發現資源的時機;fetchpriority 改變資源的優先順序。
| 對比維度 | preload | fetchpriority |
|---|---|---|
| 主要作用 | 讓 "late-discovered" 資源提早被瀏覽器看到 | 調整資源在下載佇列中的優先順序 |
| 適用情境 | CSS 背景圖、字體、動態載入的資源 | HTML 中已明確宣告的 LCP 圖片 |
| 能否突破 Tight Mode | 否(preload 的圖片仍是 Low priority) | 是(High priority 在 Tight Mode 可以下載) |
| 可組合使用 | 是 | 是(在 link[rel=preload] 上加 fetchpriority="high") |
如果你的 LCP 元素是直接寫在 HTML 的 <img>,只需要 fetchpriority="high",不需要 preload。反過來,如果 LCP 圖片是 CSS 背景圖或透過 JS 動態插入,瀏覽器看不到 HTML 屬性,這時候用 <link rel="preload" as="image" fetchpriority="high"> 才有效。
關於 preload 和 prefetch 的完整用法差異,可以參考 Critical Rendering Path 的優化策略。
如何用 DevTools 確認 fetchpriority 是否生效
加了 fetchpriority="high" 之後,最直接的驗證方式是在 Chrome DevTools 的 Network 面板查看圖片請求的 Priority 欄位是否從 Low 變為 High,步驟如下:
- 打開 DevTools → Network 面板
- 右鍵點擊欄位標題,勾選顯示 Priority 欄
- 重新載入頁面
- 找到 LCP 圖片的請求,確認 Priority 欄顯示
High而不是Low
如果加了屬性但 Priority 欄仍然顯示 Low,最常見的原因是:這張圖片是透過 CSS 載入的(background-image),HTML 屬性加在 CSS 背景圖上沒有作用,瀏覽器看不到。
另一個確認方式是在 DevTools 的 Performance 面板跑一次錄製,找到圖片請求的 timing bar,確認 priority change 發生的時機點,或是完全沒有 priority change(代表從一開始就是 High)。
設了 fetchpriority 但 LCP 仍慢的常見根因
fetchpriority 只能解決「優先順序低導致等待」這個問題。如果根因不是這個,加了屬性也不會有效果。以下是最常見的五個根因: 關於行動裝置 LCP的特殊考量,可以參考行動裝置 LCP 優化指南。
- TTFB 太高:伺服器回應慢,圖片下載的起跑點就慢了。fetchpriority 無法加速 TTFB。需要從伺服器端或 CDN 解決,參考 TTFB 優化。
- 圖片太大:即使是 High priority,下載一張 1.5MB 的圖片還是需要時間。fetchpriority 是讓下載更早開始,不是讓下載更快完成。需要搭配圖片壓縮和正確格式。
-
多張圖片都設了 high:優先順序的相對性讓多個 high 互相抵消。回去確認頁面上只有一個
fetchpriority="high"。 -
CSS background-image:HTML 屬性加在不存在的 HTML 元素上,瀏覽器無法感知。改用
<link rel="preload" as="image" fetchpriority="high">。 - 加在錯誤的元素:你認為是 LCP 的元素不一定是瀏覽器判定的 LCP 元素。先用 DevTools 或 Lighthouse 確認真正的 LCP 元素,再加屬性。如何識別可以參考 LCP 元素識別。
各框架的實作方式:WordPress、Angular、Next.js
主流框架和 CMS 大多已在圖片組件層面自動加上 fetchpriority="high",只需要啟用對應的 priority 選項即可;直接使用原生 <img> 標籤的情況才需要手動設定。
WordPress 6.3+
WordPress 6.3 開始,Core 的圖片輸出邏輯會自動在偵測到的 LCP 圖片上加 fetchpriority="high"。如果你需要手動控制,可以用 wp_get_attachment_image() 傳入 fetchpriority 參數,或使用 wp_img_tag_add_fetchpriority_attr 過濾器。
<?php
// 手動在 LCP 圖片上加 fetchpriority
echo wp_get_attachment_image(
$attachment_id,
'full',
false,
array( 'fetchpriority' => 'high' )
);
?>
Angular NgOptimizedImage
Angular 的 NgOptimizedImage 指令會自動在帶有 priority 屬性的圖片上加 fetchpriority="high":
<!-- Angular template: 加 priority 即自動設 fetchpriority="high" -->
<img ngSrc="hero.jpg" priority width="1200" height="630" alt="Hero">
Next.js
Next.js 的 next/image 組件有 priority prop,設為 true 時自動加 fetchpriority="high" 並移除 lazy loading:
// Next.js: priority prop 自動設 fetchpriority="high"
import Image from 'next/image'
export default function Hero() {
return (
<Image
src="/hero.jpg"
width={1200}
height={630}
alt="Hero"
priority // 加這個就夠了
/>
)
}
如果你在 Next.js 裡用原生 <img> 而不是 next/image,就需要手動加 fetchpriority="high"。這個情況在 App Router 的 Server Components 裡比較常見。
實驗記錄:加 fetchpriority 前後,LCP 從 3.8s 到 2.1s
測試環境:Next.js 14 (App Router) + Vercel 部署,頁面包含一個 1024×576 的 WebP hero 圖片(約 180KB),沒有 render-blocking JS,有兩個外部 CSS 字體。
| 指標 | 加 fetchpriority 前 | 加 fetchpriority 後 | 變化 |
|---|---|---|---|
| LCP (Lighthouse) | 3.8s | 2.1s | -45% |
| LCP (WebPageTest) | 4.2s | 2.3s | -45% |
| 圖片 Priority in waterfall | Low → High (layout 後才升) | High (從一開始) | 消除 priority bump |
| 圖片等待灰色段 (stall) | 約 1.4s | 約 0.1s | -93% |
| FCP | 1.2s | 1.2s | 無變化 |
FCP 沒有變化這點值得注意:fetchpriority 只影響這張圖片的下載時間,不影響 HTML parsing 和初始渲染。如果你想同時加速 FCP,需要解決的是 render-blocking 資源,不是圖片優先順序。
另一個值得記錄的細節:這個測試的字體載入用的是 font-display: swap,Tight Mode 結束時間(DOM Interactive)大約在 1.8s。沒有 fetchpriority 時,圖片在 Tight Mode 中等了足足 1.4 秒;加了 fetchpriority 後,圖片在頁面載入 0.4s 就開始下載了。
瀏覽器支援現況(2024 年後的完整支援)
2026 年的今天,fetchpriority 已在 Chrome、Edge、Safari、Firefox 所有主流桌面和行動瀏覽器上完整支援,可以無條件在生產環境使用,不需要 polyfill 或 feature detection。
- Chrome/Edge:從 2022 年 Chrome 102 開始支援
- Safari:iOS/macOS Safari 17.2(2023 年底)開始支援
- Firefox:Firefox 132(2024 年 10 月)正式加入
這個屬性在不支援的舊瀏覽器中會被忽略,不會有任何副作用。換句話說,加了 fetchpriority="high" 在任何情況下都是安全的,支援的瀏覽器會用,不支援的瀏覽器跳過。
全球大約 95% 以上的瀏覽器流量現在都可以受益於這個屬性。台灣市場以 Chrome 和 Safari 為主,實際覆蓋率更高。
如果你還沒在 LCP 圖片上加這個屬性,這是今天可以做的最簡單的效能改善。一行 HTML 屬性,不需要後端改動,不需要 JS,不需要 build step,瀑布圖裡的那個灰色等待段就消失了。
參考資料