在上一篇 HTML picture 與 source 標籤實作要點:Art Direction 裁切、AVIF/WebP 降級與深色模式適配 與 HTML 行內與區塊元素解析:深入置換元素(img/input)的盒模型與排版邊界 中,我們介紹瞭如何透過 <picture> 與 <source> 解決跨裝置不同長寬比裁切(Art Direction)與 AVIF / WebP 格式降級。而在網站中佔比高達 80% 以上的文章插圖、商品卡片與圖庫列表,其長寬比在桌機與手機上完全相同,真正的效能課題是「如何避免 390px 寬的手機下載一張 2400px 的桌機大圖」。本文將依據 WHATWG HTML Living Standard 規範,解析 HTML picture 與 srcset 在解析度切換(Resolution Switching)中的計算公式、瀏覽器預載掃描器(Preload Scanner)為何需要 sizes 屬性、新標準 sizes="auto" 的應用,並整理國外開發者論壇最常討論的 6 個 srcset 排查問答。
常見問題(Q&A)與程式碼範本快速跳轉
如果你目前正在處理手機端依然載入大尺寸圖片、縮放視窗時 srcset 沒有切換,或對 sizes 為什麼不能寫 % 百分比感到困惑,可以直接點擊下方連結跳轉至對應解答與程式碼範本:
- 國外論壇高頻問答(Q&A):
- Q1:為什麼寫了 srcset 的 w 描述符卻沒寫 sizes 時,三欄卡片縮圖在手機與桌機上都抓 100vw 最大圖?
- Q2:為什麼在桌機將瀏覽器視窗從寬拉窄時,Chrome 沒有自動切換去下載小尺寸的圖片?
- Q3:為什麼我的手機螢幕寬度明明只有 390px,瀏覽器卻跳過 400w 與 800w,直接下載 1200w 的圖片?
- Q4:可以在同一個 srcset 屬性裡面同時混寫 400w(寬度描述符)與 2x(像素密度描述符)嗎?
- Q5:為什麼在 sizes 屬性裡面寫百分比(例如 sizes="50%")會完全失效?
- Q6:實務專案中該準備幾個 srcset 圖片寬度斷點,才不會讓雲端儲存空間與建置時間暴增?
- 生產環境速查表與程式碼範本:
如果不趕時間,建議順著下方章節往下讀,我們將從瀏覽器預載掃描器(Preload Scanner)的運作時機開始,逐一講解 srcset 的 w 與 x 描述符差異、sizes 目標像素計算公式與新標準 sizes="auto" 的底層原理。
一、為什麼瀏覽器需要 srcset 與 sizes 來提前計算圖片尺寸?
1. 原本常見的單一高解析度大圖寫法與頻寬浪費
為了讓圖片在 4K 桌機螢幕與高像素密度(Retina)裝置上看起來清晰不模糊,許多專案會直接匯出一張 1920px 或 2400px 寬的高解析度圖片放進 <img>:
<!-- 原本常見的單一大圖寫法:所有裝置被迫下載同一張 1.2 MB 的 2400px 原圖 -->
<img
src="/images/article-cover-2400.jpg"
alt="雲端原生微服務監控儀表板"
width="2400"
height="1350"
style="width: 100%; height: auto;"
>
這種寫法雖然在 CSS 的 width: 100% 控制下能自動縮放外觀,但在網路傳輸與記憶體層面卻造成兩個問題:
- 行動端頻寬與時間浪費:在三欄卡片列表中,一張顯示寬度僅
350px的卡片縮圖,若下載2400px的原圖,多傳輸了將近 80%~90% 不會被肉眼看見的多餘像素。 - 行動裝置影像解碼(Image Decoding)記憶體壓力:瀏覽器下載
2400x1350的圖片後,在解碼展開為未壓縮點陣圖(RGBA 每像素 4 Bytes)時,單張圖就會在手機記憶體中佔用2400 × 1350 × 4 ≈ 12.96 MB的緩衝空間。
2. 瀏覽器預載掃描器(Preload Scanner)與選圖公式:目標像素 = sizes 渲染寬度 × DPR
許多前端工程師在初次接觸 srcset 與 sizes 時,常會有一個疑問:「既然我已經在外部 CSS 檔案裡寫了 .card-img { width: 33.3vw; },為什麼瀏覽器不能自己看 CSS 就好,還要我在 HTML 的 sizes 裡再寫一次寬度?」
原因在於瀏覽器的預載掃描器(Preload Scanner)運作時機:
當瀏覽器剛收到 HTML 文件時,外部的 .css 樣式表與 .js 腳本通常還在網路上下載、尚未完成解析(CSSOM 尚未建立)。為了不讓圖片傻傻等待 CSS 下載完才開始請求,瀏覽器的 Preload Scanner 會在 HTML 解析初期就提前掃描 <img> 標籤並發起圖片下載。
在沒有 CSSOM 的情況下,瀏覽器只知道兩件事:
- 當前視窗寬度(Viewport Width,即
100vw)。 - 當前螢幕的裝置像素比(Device Pixel Ratio,
DPR)(例如一般桌機螢幕為1x,MacBook Retina 為2x,現代 iPhone 與高階 Android 手機為3x)。
因此,我們必須透過 sizes 屬性直接告訴 Preload Scanner:「在某個視窗寬度下,這張圖片預計會佔據多寬的版面槽位(Slot Width)」,瀏覽器再依據以下公式算出目標物理像素:
瀏覽器選圖計算公式:
目標物理像素 (Target Physical Pixels) = sizes 算出的槽位寬度 (CSS px) × 裝置像素比 (DPR)
接著瀏覽器便從 srcset 清單中,挑選最接近(通常優先挑選大於等於目標值)的那張 w 圖片下載。下表規格對照表整理三種解析度描述符的規格與適用場景:
規格對照表:x 描述符、w 描述符與 sizes=”auto” 比較
| 描述符與屬性組合 | 語法範例 | 預設值與規範限制 | 最佳適用場景 |
|---|---|---|---|
x 像素密度描述符 | srcset="logo-1x.webp 1x, logo-2x.webp 2x, logo-3x.webp 3x" | 不得與 sizes 或 w 描述符混用;未標註者預設為 1x | 固定 CSS 寬度的小型元件(如導覽列 160px 品牌 Logo、48px 使用者頭貼) |
w 寬度描述符 + 明確 sizes | srcset="img-400.webp 400w, img-800.webp 800w" sizes="(max-width: 768px) 100vw, 50vw" | 若寫了 w 卻漏寫 sizes,瀏覽器預設以 sizes="100vw" 計算;sizes 內嚴禁使用 % | 首屏關鍵圖片(LCP) 或隨視窗流體縮放的橫幅、多欄卡片圖片 |
w 寬度描述符 + sizes="auto" | loading="lazy" srcset="img-400.webp 400w, img-800.webp 800w" sizes="auto, (max-width: 768px) 100vw, 50vw" | 必須搭配 loading="lazy"(WHATWG 標準,Chrome 126+ / Safari 18.4+ 支援) | 首屏以下(Below-the-fold)的懶載入圖片,由瀏覽器在排版完成後自動帶入真實渲染寬度 |
下圖展示瀏覽器 Preload Scanner 如何讀取 sizes 媒體條件、乘上裝置 DPR,並從 srcset 的 w 候選清單中選出最佳圖片:
圖:瀏覽器 Preload Scanner 結合 sizes、DPR 與 srcset (w 描述符) 之目標圖片計算流程圖
二、三大常見排版場景的 srcset 與 sizes 程式碼範本
下圖提供選擇 x 像素密度描述符、w 寬度描述符與 sizes="auto" 的實務決策樹:
圖:響應式圖片 x 描述符、w 描述符與 sizes=auto 選型決策樹
三大實務場景程式碼實作
1. 場景一:固定寬度元件(品牌 Logo 與頭貼)— 使用 x 像素密度描述符
當圖片在所有螢幕尺寸下的 CSS 顯示寬度都是固定的(例如導覽列上的品牌 Logo 永遠固定為 150px × 40px),不需要使用 w 與 sizes,直接用 1x、2x、3x 像素密度描述符最簡潔直觀:
<!-- 固定 150x40 CSS 像素的品牌 Logo:依螢幕 DPR 自動挑選 150w / 300w / 450w 圖檔 -->
<img
src="/images/brand-logo-150w.webp"
srcset="/images/brand-logo-150w.webp 1x,
/images/brand-logo-300w.webp 2x,
/images/brand-logo-450w.webp 3x"
alt="企業品牌標誌"
width="150"
height="40"
>
2. 場景二:RWD 多欄卡片網格(手機單欄、平板雙欄、桌機三欄)— 使用 w 描述符搭配 sizes
假設我們有一個常見的響應式文章卡片列表:
- 手機版(
<= 640px):單欄滿版顯示,扣除左右各16px邊距後寬度為calc(100vw - 32px)。 - 平板版(
<= 1024px):雙欄並排顯示,每張卡片約佔calc(50vw - 24px)。 - 桌機版(
> 1024px):最大容器寬度限制為1200px的三欄並排,每張卡片固定約380px。
此時透過 srcset 的 w 描述符(標註每張圖檔真實的像素寬度)搭配 sizes(由上而下匹配第一個符合的媒體條件),即可讓各裝置精準下載最合適的尺寸:
<img
src="/images/card-cover-800.webp"
srcset="/images/card-cover-400.webp 400w,
/images/card-cover-800.webp 800w,
/images/card-cover-1200.webp 1200w"
sizes="(max-width: 640px) calc(100vw - 32px),
(max-width: 1024px) calc(50vw - 24px),
380px"
alt="分散式資料庫讀寫分離架構圖"
width="800"
height="450"
loading="lazy"
decoding="async"
style="width: 100%; height: auto; display: block;"
>
3. 場景三:結合 picture 格式降級與 srcset 多解析度矩陣的複合寫法
當我們想要**同時享有 <picture> 的「AVIF ➔ WebP ➔ JPEG 新格式降級」與 srcset 的「多解析度自動挑選」**時,可以在 <picture> 的每個 <source> 上同時配置 type、srcset(含 w 描述符)與 sizes:
<picture>
<!-- 第一順位:支援 AVIF 的瀏覽器,依 sizes 與 DPR 從三種寬度中挑選一張 AVIF -->
<source
type="image/avif"
srcset="/images/hero-640.avif 640w,
/images/hero-1280.avif 1280w,
/images/hero-1920.avif 1920w"
sizes="(max-width: 1280px) 100vw, 1280px"
>
<!-- 第二順位:支援 WebP 的瀏覽器,從三種寬度中挑選一張 WebP -->
<source
type="image/webp"
srcset="/images/hero-640.webp 640w,
/images/hero-1280.webp 1280w,
/images/hero-1920.webp 1920w"
sizes="(max-width: 1280px) 100vw, 1280px"
>
<!-- 最終保底:傳統 JPEG 與基礎屬性宣告 -->
<img
src="/images/hero-1280.jpg"
srcset="/images/hero-640.jpg 640w,
/images/hero-1280.jpg 1280w,
/images/hero-1920.jpg 1920w"
sizes="(max-width: 1280px) 100vw, 1280px"
alt="年度開發者大會主題橫幅"
width="1920"
height="1080"
fetchpriority="high"
style="width: 100%; height: auto; display: block;"
>
</picture>
三、WHATWG 新標準:loading=”lazy” 搭配 sizes=”auto” 省去手算斷點
在大型元件庫中,一個通用的 <ImageCard> 元件可能會被放進雙欄、三欄、四欄或側邊欄容器裡,元件本身很難預先知道自己在不同頁面下的 sizes 媒體查詢公式。
為了解決這個痛點,WHATWG HTML Living Standard 正式納入 sizes="auto" 規範(已在 Chrome 126+、Edge 126+、Safari 18.4+ 支援):
因為設定了 loading="lazy" 的懶載入圖片,只會在捲動接近可視範圍時才發起請求,而此時 CSS 樣式表早已解析完畢、瀏覽器已經知道該 <img> 在畫面上的真實渲染像素寬度(Concrete Object Size)!
只要將 sizes 開頭寫上 auto(並在逗號後面保留傳統 sizes 數值作為舊版瀏覽器退路),支援的瀏覽器就會自動拿 CSS 算出的真實寬度乘上 DPR 去選圖:
<!-- 現代懶載入圖片推薦寫法:sizes 以 auto 開頭,後方保留舊版瀏覽器 fallback -->
<img
loading="lazy"
src="/images/gallery-800.webp"
srcset="/images/gallery-400.webp 400w,
/images/gallery-800.webp 800w,
/images/gallery-1200.webp 1200w"
sizes="auto, (max-width: 768px) 100vw, 33vw"
width="800"
height="600"
alt="作品集展示卡片"
style="width: 100%; height: auto;"
>
四、國外開發者論壇(Stack Overflow / Reddit)最常遇到的 6 個 srcset 與 sizes 疑問
以下彙整 Stack Overflow、Reddit(r/webdev、r/Frontend)與 WebPageTest 效能論壇中,開發者針對 srcset 與 sizes 最常提問的 6 個問題:
Q1:為什麼寫了 srcset 的 w 描述符卻沒寫 sizes 時,三欄卡片縮圖在手機與桌機上都抓 100vw 最大圖?
- 常見情境:開發者在桌機四欄商品列表的
<img>寫了srcset="item-300.jpg 300w, item-600.jpg 600w, item-1200.jpg 1200w",但省略了sizes屬性。結果在1440px的桌機上,每張實際只顯示300px寬的小卡片,全部去下載了1200w的大圖! - 底層原因:根據 WHATWG HTML 規範,當
srcset使用w寬度描述符且未明確指定sizes時,sizes的預設值為100vw(即假設圖片佔滿整個視窗寬度)。因此在1440px視窗下,瀏覽器會以1440px為基準去挑選1200w。 - 解法:凡是在
srcset中使用了w描述符,就必須同時搭配sizes屬性(如sizes="(max-width: 768px) 50vw, 300px",或對懶載入圖片加上sizes="auto, ...")。
Q2:為什麼在桌機將瀏覽器視窗從寬拉窄時,Chrome 沒有自動切換去下載小尺寸的圖片?
- 常見情境:開發者寫好
srcset與sizes後,在全螢幕桌機打開網頁(此時瀏覽器載入了1600w大圖),接著用滑鼠把瀏覽器視窗拉窄到400px手機寬度,打開 Network 面板卻發現瀏覽器完全沒有去請求400w的小圖,懷疑srcset寫錯了。 - 底層原因:這是 Chromium 與其他現代瀏覽器刻意設計的頻寬保護機制!因為
srcset內的所有圖片被視為「同一構圖的不同解析度副本」,既然瀏覽器記憶體快取(Memory Cache)中已經擁有一張高畫質的1600w圖片,直接將現有的1600w縮小顯示在400px容器內完全不花一毛網路流量;若為了視窗變窄又發起 HTTP 請求去下載一張畫質較低的400w圖片,反而浪費頻寬。(反之,若從窄視窗拉寬到大視窗,瀏覽器才會補抓大圖。) - 解法:在 Chrome DevTools 測試
srcset時,請遵循以下三個步驟:- 打開 DevTools 切換至 Network 分頁,勾選 Disable cache(停用快取)。
- 點擊左上角開啟 Device Toolbar(裝置模擬器,
Ctrl+Shift+M),先選好目標手機型號(如 iPhone 14 Pro)。 - 按下
Ctrl+R重新整理頁面,即可觀察到瀏覽器從零開始評估並下載對應尺寸的圖檔。
Q3:為什麼我的手機螢幕寬度明明只有 390px,瀏覽器卻跳過 400w 與 800w,直接下載 1200w 的圖片?
- 常見情境:開發者在 iPhone(視窗寬度
390px)上實測滿版圖片(sizes="100vw"),預期瀏覽器會挑選400w的圖片,但從 Network 面板看卻發現它下載了1200w。 - 底層原因:混淆了 CSS 邏輯像素(CSS Pixels) 與 裝置物理像素(Physical Pixels)。現代 iPhone 與旗艦 Android 手機的螢幕像素密度(
window.devicePixelRatio, DPR)普遍為3。當sizes="100vw"時,瀏覽器計算出的目標物理像素為390 CSS px × 3 (DPR) = 1170 物理像素,為了在 Retina 螢幕呈現銳利畫質,自然會挑選最接近的1200w。 - 解法:這是完全符合規範的正常行為。若圖片屬於視覺容忍度較高的背景或次要配圖、且希望在 3x 手機上強制限制傳輸體積,可調整
sizes的最大上限(例如將滿版卡片設為最大800px上限,或交由高壓縮比的 AVIF 格式降低1200w的檔案大小)。
Q4:可以在同一個 srcset 屬性裡面同時混寫 400w(寬度描述符)與 2x(像素密度描述符)嗎?
- 常見情境:開發者嘗試寫出
srcset="small.jpg 400w, retina.jpg 2x",結果 W3C HTML Validator 直接報錯,且部分候選圖片被瀏覽器完全忽略。 - 底層原因:根據 WHATWG HTML 規範,同一個
srcset屬性中的所有候選項目必須使用同一種描述符系統:要嘛全部使用w寬度描述符,要嘛全部使用x像素密度描述符,嚴禁混用;此外,若使用了x描述符,則不得在同一個標籤上宣告sizes屬性。 - 解法:流體縮放圖片統一全部寫
w描述符(如400w, 800w, 1200w)並搭配sizes;固定寬度小圖示統一全部寫x描述符(如1x, 2x)並省略sizes。
Q5:為什麼在 sizes 屬性裡面寫百分比(例如 sizes=”50%”)會完全失效?
- 常見情境:因為圖片在父容器內設定了 CSS
width: 50%,開發者很直覺地寫出sizes="(max-width: 768px) 100%, 50%",結果瀏覽器完全無視這段設定,直接退回預設的100vw。 - 底層原因:根據 WHATWG 規範,
sizes中的<source-size-value>長度值明確禁止使用百分比(%)!因為百分比是相對於「父容器寬度」計算的,而在瀏覽器 Preload Scanner 執行當下,DOM 樹與外部 CSS 根本還沒完成排版,瀏覽器無從得知父容器有多寬,唯一已知的只有視窗寬度(vw)。 - 解法:在
sizes中請一律使用vw(視窗寬度單位)、px/rem/em(絕對或字級相對單位),或搭配calc()(例如雙欄扣除間距寫成calc(50vw - 24px))。
Q6:實務專案中該準備幾個 srcset 圖片寬度斷點,才不會讓雲端儲存空間與建置時間暴增?
- 常見情境:有些教學示範每隔
100px就切一張圖(300w, 400w, 500w ... 2000w共 18 張圖),若再乘上 AVIF、WebP、JPG 三種格式,上傳一張圖就會在 S3 產生 54 個檔案,且大幅降低 CDN 快取命中率(Cache Hit Ratio)。 - 底層原因:斷點切得太密,會讓不同螢幕尺寸的使用者各自請求不同 URL,導致 CDN 邊緣節點快取碎片化。
- 解法:在生產環境中,每張圖片準備 3~5 個具有顯著級距的寬度斷點即能取得頻寬與 CDN 快取命中率的最佳平衡:
- 通用內容圖片/橫幅(4 段式):
400w(1x 手機 / 縮圖)、800w(2x 手機 / 平板)、1200w(3x 手機 / 一般筆電)、1920w(高解析度桌機)。 - 卡片縮圖(3 段式):
360w、720w、1080w。
- 通用內容圖片/橫幅(4 段式):
總結
善用 HTML picture 與 srcset 的解析度切換機制,能在不犧牲高解析度螢幕畫質的前提下,大幅降低行動裝置的圖片傳輸量:
- 理解 Preload Scanner 公式:瀏覽器以
sizes 槽位寬度 × DPR決定目標物理像素,因此使用w描述符時務必提供準確的sizes(且切勿在sizes內寫%百分比)。 - 區分
x與w描述符:固定寬度的 Logo 與頭貼使用1x, 2x, 3x;隨版面縮放的卡片與橫幅使用400w, 800w, 1200w搭配sizes。 - 導入新標準
sizes="auto":針對設定了loading="lazy"的非首屏圖片,可加上sizes="auto, ..."讓瀏覽器直接使用排版後的真實寬度。 - 正確測試快取行為:在 Chrome DevTools 驗證
srcset時,務必勾選Disable cache並先切好手機模擬器再重新整理。
參考資料與延伸閱讀
- WHATWG HTML Living Standard:The img element (srcset and sizes specifications)
- MDN Web Docs:Responsive images (srcset, sizes, and picture guide)
- MDN Web Docs:HTMLImageElement.srcset API 規格說明
- MDN Web Docs:HTMLImageElement.sizes API 規格說明
- MDN Web Docs:Lazy loading 官方技術規格
- 延伸閱讀:別再只用 div 與 span:HTML 內聯與區塊元素(Inline vs Block)底層渲染邏輯與常見語法地雷
- 延伸閱讀:WCAG 無障礙網頁規範總覽:POUR 四大原則與 A / AA / AAA 等級檢核


發表迴響