Skip to content

srcset 與 sizes 怎麼寫?響應式圖片不多下載一個位元

響應式圖片:srcset 與 sizes 的寫法

手機的螢幕只有 400 像素寬,卻下載了一張 2000 像素的圖,這是行動裝置 載入速度 最常見的浪費。

srcset 提供多種尺寸,sizes 告訴瀏覽器 圖片實際會顯示多寬,兩者搭配才能讓它挑對。

最基本的寫法

html
<img
  src="/photo-800.webp"
  srcset="/photo-400.webp 400w, /photo-800.webp 800w, /photo-1600.webp 1600w"
  sizes="(max-width: 600px) 100vw, 800px"
  alt="說明文字"
  width="800"
  height="600"
  loading="lazy"
  decoding="async"
>
屬性意義
src不支援 srcset 時的備援
srcset有哪些尺寸可選,400w 表示 這個檔案本身是 400 像素寬
sizes圖片在版面上 實際會顯示多寬

w 與 x 兩種描述元

w 描述元:寬度會變的圖片

html
<img
  srcset="/photo-400.webp 400w, /photo-800.webp 800w, /photo-1600.webp 1600w"
  sizes="(max-width: 600px) 100vw, 800px"
  src="/photo-800.webp"
  alt="說明文字"
>

w 描述的是「檔案的實際像素寬度」

400w 不是「螢幕 400px 時用這張」,而是「這個檔案本身是 400 像素寬」。

瀏覽器拿 sizes 算出需要多寬,再對照 srcset 挑最合適的檔案。

x 描述元:顯示寬度固定的圖片

html
<!-- 標誌永遠顯示 200px 寬,只需要處理高解析度螢幕 -->
<img
  srcset="/logo-200.webp 1x, /logo-400.webp 2x, /logo-600.webp 3x"
  src="/logo-200.webp"
  alt="網站標誌"
  width="200"
  height="60"
>
w 描述元x 描述元
描述什麼檔案的像素寬度裝置像素比倍率
需要 sizes
適合寬度隨版面變化的圖顯示寬度固定的圖
典型內容圖、主視覺標誌、頭像、圖示

判斷方式

問一句話:這張圖的顯示寬度會隨螢幕變化嗎?

  • 會(滿版主視覺、內容圖)→ 用 wsizes
  • 不會(40px 的頭像、200px 的標誌)→ 用 x

sizes 是必填

沒寫 sizes 是最常見的錯誤

html
<!-- 錯誤:瀏覽器假設圖片佔滿整個視窗寬度 -->
<img srcset="/photo-400.webp 400w, /photo-1600.webp 1600w" src="/photo-800.webp" alt="說明">

sizes 的預設值是 100vw。所以在 1920 像素寬的桌機上,即使圖片實際只顯示 300 像素寬,瀏覽器仍會挑 1600 那張。

寫了多種尺寸卻沒省下任何流量。

sizes 的語法

sizes="(媒體條件) 寬度, (媒體條件) 寬度, 預設寬度"

從左到右比對,第一個成立的就採用,最後一項是沒有媒體條件的預設值:

html
sizes="(max-width: 600px) 100vw,
       (max-width: 1000px) 50vw,
       800px"

讀法:

條件圖片顯示寬度
視窗 ≤ 600px100% 視窗寬度
視窗 ≤ 1000px50% 視窗寬度
其他(> 1000px)固定 800px

常見的 sizes 寫法

html
<!-- 內容區固定最大寬度的文章圖 -->
sizes="(max-width: 768px) 100vw, 768px"

<!-- 兩欄式版面 -->
sizes="(max-width: 600px) 100vw, 50vw"

<!-- 三欄式的商品列表 -->
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 33vw"

<!-- 永遠滿版的主視覺 -->
sizes="100vw"

<!-- 扣掉左右內距的滿版 -->
sizes="calc(100vw - 2rem)"

sizes 要跟 CSS 斷點一致

這是這個機制最麻煩的地方

css
.article img {
  width: 100%;
  max-width: 768px;
}

@media (max-width: 768px) {
  .article {
    padding: 0 1rem;
  }
}

對應的 sizes 應該是:

html
sizes="(max-width: 768px) calc(100vw - 2rem), 768px"

樣式改了斷點,標記也必須同步改。 兩邊很容易不一致,而且不一致時完全不會有任何錯誤提示。

用框架的圖片元件避開這個問題

Nuxt、Next 這類框架的圖片元件會依設定自動產生 srcsetsizes。自己維護的話,至少把 sizes 抽成常數,不要散落在各個模板裡。

js
// 一處定義,各處引用
export const ARTICLE_IMAGE_SIZES = '(max-width: 768px) calc(100vw - 2rem), 768px';

要準備幾種尺寸

三到四種就夠

尺寸涵蓋
400w手機(1x)
800w手機(2x)、平板
1600w桌機、高解析度螢幕

再多的邊際效益很低,還讓檔案數量與建置時間膨脹。

如果同時做 多種格式 ,檔案數會是「格式數 × 尺寸數」,三種格式配三種尺寸就是九個檔案,一定要自動化

瀏覽器不保證挑最小的

它有自己的判斷

瀏覽器選擇時會考慮:

  • 裝置像素比(2x 螢幕需要兩倍的像素)
  • 目前的網路狀況
  • 已經快取的版本(有 1600 的快取時可能直接用它)

所以同一組標記在不同裝置、不同時間挑的可能不同,而且它 有權挑比計算結果更大的那張

這也是為什麼不要靠 srcset 做「藝術指導」(不同螢幕顯示不同構圖的圖),那要用 <picture>media 屬性明確指定。

藝術指導要用 picture

srcset 是「同一張圖的不同尺寸」,<picture>media 才能「不同螢幕給不同的圖」:

html
<picture>
  <!-- 手機:裁切成方形,主體更明顯 -->
  <source media="(max-width: 600px)" srcset="/hero-square.webp">
  <!-- 桌機:寬幅構圖 -->
  <img src="/hero-wide.webp" alt="主視覺" width="1600" height="600">
</picture>
需求用什麼
同一張圖的不同尺寸srcset + sizes
不同螢幕給不同構圖<picture> + media
不同格式的降級<picture> + type

完整的組合寫法

格式降級加響應式尺寸:

html
<picture>
  <source
    type="image/avif"
    srcset="/photo-800.avif 800w, /photo-1600.avif 1600w"
    sizes="(max-width: 768px) 100vw, 768px"
  >
  <img
    src="/photo-800.webp"
    srcset="/photo-800.webp 800w, /photo-1600.webp 1600w"
    sizes="(max-width: 768px) 100vw, 768px"
    alt="說明文字"
    width="800"
    height="600"
    loading="lazy"
    decoding="async"
  >
</picture>

這種標記不該手寫

上面只是兩種格式配兩個尺寸,就已經四個檔案、兩份要同步的 sizes。實際專案通常更多,手寫一定會有忘記同步的時候。用元件或建置工具產生。

怎麼驗證有沒有生效

方式怎麼做
DevTools 的 Network看實際下載的是哪個檔案
DevTools 的 Elements滑到 <img> 上,提示會顯示「Current source」
改變視窗寬度重載確認不同寬度下挑的檔案不同
Lighthouse「Properly size images」會標出過大的圖

一定要重新載入

瀏覽器不會因為視窗變寬就重新下載更大的圖(那樣太浪費)。測試時要 改變寬度後重新載入

反之,視窗變小時它也不會換小圖,已經下載的就繼續用。

自動化產生

js
import sharp from 'sharp';

const SIZES = [400, 800, 1600];

for (const width of SIZES) {
  await sharp('src/photo.jpg')
    .resize(width)
    .webp({ quality: 82 })
    .toFile(`dist/photo-${width}.webp`);
}

這個站的做法

這個站沒有用 srcset

理由是橫幅圖是 SVG,向量圖在任何尺寸下都清晰,而且只有幾 KB,沒有「下載過大的圖」這個問題。

markdown
![說明文字](/images/seo/image-responsive-banner.svg){ width=1200 height=400 fetchpriority="high" }

這是一個值得記住的取捨:能用 SVG 解決的圖形,比做一整套響應式尺寸簡單得多。srcset 是照片才需要的工具。

LinkCard 元件的圖同樣是 SVG,所以也只寫死了尺寸:

html
<img :src="image" :alt="imgAlt" width="1200" height="400" loading="lazy">

檢查清單

項目標準
w 描述元時有寫 sizes必備
sizes 與 CSS 的實際顯示寬度一致必備
顯示寬度固定的圖用 x 描述元建議
準備三到四種尺寸建議
src 當備援必備
widthheight 避免 版面跳動必備
藝術指導用 <picture>media必備
由元件或建置工具產生建議
已用 Network 面板驗證挑對檔案建議

常見問題

w 和 x 兩種描述元差在哪?

w 描述的是圖片檔案本身的像素寬度,需要搭配 sizes 才知道要挑哪一張,適合寬度會隨版面變化的圖片。x 描述的是裝置像素比倍率,不需要 sizes,適合顯示寬度固定的圖片例如:標誌與頭像。

沒寫 sizes 會怎樣?

瀏覽器會假設圖片佔滿整個可視範圍寬度,於是挑一張過大的圖。這是響應式圖片最常見的錯誤,寫了多種尺寸卻沒有節省任何流量。用 w 描述元時 sizes 必填。

sizes 要跟 CSS 的斷點一致嗎?

要,否則瀏覽器算出的寬度會與實際顯示不符。麻煩的是這代表樣式改了斷點就必須同步改標記,兩邊很容易不一致。用建置工具或框架的圖片元件產生可以避免這個問題。

要準備幾種尺寸?

三到四種就夠。太多種的邊際效益很低,還讓檔案數量與建置時間膨脹。實務上常用的是 400、800 與 1600 像素三段,涵蓋手機、平板與含高解析度螢幕的桌機。

瀏覽器一定會挑最小的那張嗎?

不一定。它會考慮裝置像素比、目前的網路狀況,甚至已經快取的版本。所以同一組標記在不同裝置上挑的可能不同,而且瀏覽器有權挑比計算結果更大的那張。

延伸閱讀

參考資料:MDN:響應式圖片