响应式图片
基于 HTML Living Standard · 核于 2026-06
速查
- 响应式图片解决两件事:分辨率切换(同图不同尺寸,省流量)、高 DPI 适配(视网膜屏要更大图)
- 两套描述符二选一,不可混用:宽度描述符
480w(配sizes)/ 像素密度描述符2x(不配sizes) srcset="a.jpg 480w, b.jpg 800w":列出候选图 + 各自固有宽度(w= 真实像素宽)sizes="(max-width: 600px) 100vw, 50vw":声明图在各条件下占多宽(最后一项无条件作默认)sizes能用vw/px等长度,不能用百分比;浏览器取第一个匹配的条件,顺序很重要- 密度描述符:
srcset="a.jpg, a-2x.jpg 2x"+ CSS 定死显示尺寸,适合「显示尺寸固定、只换清晰度」 - 选图逻辑:浏览器结合视口 / 像素密度 / 网络,算出需要的图宽,挑
srcset里最接近且不小于的那张 - 必须用 HTML 不能用 JS:浏览器在解析 CSS / JS 之前就预加载图片,JS 改
src只会白下两张 src仍要写:作为不支持srcset的旧浏览器兜底
为什么需要响应式图片
设备屏幕从 320px 的手机到 4K 显示器,分辨率从 1× 到 3× 不等。如果全站只发一张大图:
- 手机用户白下大图:流量与电量都浪费,慢网下首屏卡很久;
- 位图放大变糊:一张 480px 的位图被拉到 1200px 显示会发虚。
理想状态是:浏览器根据当前设备自动挑最划算的那一份。这正是 srcset / sizes 与 <picture> 要解决的。其中 <img> 上的 srcset / sizes 负责「同一张图、不同尺寸 / 清晰度」的分辨率切换;而「窄屏换裁切」「换格式」属于 art direction,归 <picture> 管,见 下一页。
为什么不能用 JS 判断视口再换图
浏览器有个「预加载扫描器」:它在主解析器开始处理 CSS / JS 之前,就抢先把图片请求发出去了——这本是为了加速。后果是:你没法「先放 <img>、用 JS 测视口宽度、再动态换小图」,因为等 JS 跑起来,原图早下完了,再换只会下两张,更糟。所以响应式图片必须由 HTML 声明、交给浏览器决策。
分辨率切换(一):宽度描述符 + sizes
最常用的一套。srcset 列出同一张图的多个尺寸版本,每个标注它的固有宽度(intrinsic width,即图片文件本身的真实像素宽);sizes 告诉浏览器这张图在页面上会占多宽:
<img
srcset="photo-480.jpg 480w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
src="photo-800.jpg"
alt="风景照"
width="1200"
height="800" />srcset:候选图 + 固有宽度
srcset="photo-480.jpg 480w, photo-800.jpg 800w"逗号分隔,每项 =「图片地址 + 空格 + 固有宽度」。这里的 480w 表示这张图本身就是 480 像素宽(不是显示宽度),w 是宽度描述符单位。用了 w 描述符,就必须搭配 sizes。
sizes:在各条件下占多宽
sizes="(max-width: 600px) 100vw, 50vw"逗号分隔,每项 =「媒体条件 + 空格 + 槽位宽度」:
(max-width: 600px) 100vw:视口 ≤ 600px 时,图占满整个视口宽(100vw);50vw:最后一项不带条件,作为默认——其余情况图占视口一半。
几条硬规则:
- 槽位宽度可用
vw/px等任意长度值,但不能用百分比(%); - 最后一项必须是「无媒体条件的默认值」;
- 浏览器只认第一个匹配为真的条件,所以条件的书写顺序很重要,从窄到宽排好。
浏览器到底怎么挑
拿到上面这段,浏览器会:
- 评估屏幕尺寸、像素密度、缩放、方向、网络速度;
- 找出
sizes里第一个为真的媒体条件,得到「这张图要占多宽」(槽位宽度); - 把槽位宽度 ×设备像素比,算出「理想图宽」;
- 在
srcset里挑最接近、且通常不小于理想图宽的那张下载;找不到精确匹配时,挑「比理想宽度大的第一张再缩小适配」。
举例:480px 视口(≤600px → 槽位 100vw = 480px),普通屏选 photo-480.jpg;1000px 视口(→ 槽位 50vw = 500px),普通屏会选 photo-800.jpg(比 500 大的第一张)。手机因此不会白下 1200px 大图。
分辨率切换(二):像素密度描述符
当图片的显示尺寸是固定的(比如一个固定 320px 宽的头像),只是想在视网膜屏上更清晰,用密度描述符更简洁——不需要 sizes:
<img
srcset="avatar-320.jpg, avatar-480.jpg 1.5x, avatar-640.jpg 2x"
src="avatar-640.jpg"
alt="用户头像"
width="320"
height="320" />配合 CSS 把显示宽度定死:
img {
width: 320px;
}工作方式:
- 第一项不写描述符,隐含
1x(标准密度屏); 2x表示「2 设备像素对 1 CSS 像素」的高 DPI 屏用这张;- 浏览器测出当前屏幕密度,下对应那张——普通屏下
avatar-320.jpg,2× 屏下avatar-640.jpg。
w 与 x 怎么选
- 图的显示宽度会随布局变化(响应式占位)→ 用
w描述符 +sizes; - 图的显示尺寸固定、只想换清晰度(头像、Logo、固定尺寸缩略图)→ 用
x描述符; - 两者绝不混写在同一个
srcset里。优先考虑w+sizes,因为它更灵活,且省流量模式下浏览器能自行降级。
src 为什么还要留着
<img srcset="…" sizes="…" src="photo-800.jpg" alt="…" />src 是不支持 srcset 的旧浏览器的兜底:它们忽略 srcset / sizes,照常加载 src 指向的图。支持的浏览器则优先用 srcset 的匹配结果。所以 src 一般填一张「中等尺寸、最普适」的版本,作为安全网。
自己写还是交给框架 / CDN
手写 srcset 容易,但生成多份尺寸的图很烦。实战里通常:
- 图片 CDN(如 Cloudinary、imgix、又拍云)按 URL 参数实时生成各尺寸 / 各格式;
- 构建期插件(Vite 的
vite-imagetools、Next.js 的<Image>、Astro 的<Image>)在打包时自动产出srcset与 AVIF / WebP。
无论用哪种,背后产出的仍是本页讲的这套 srcset / sizes / <picture> 标记——理解原理才能调对参数、排查「为什么挑错了图」。
小结
srcset 给候选、sizes 给占宽,浏览器据此自动挑分辨率;显示尺寸固定时用 x 描述符更省事。但 srcset / sizes 只能做「同一张图换尺寸」——当你想换裁切(窄屏突出主体)或换格式(AVIF 优先、JPEG 兜底)时,得用 <picture>。下一页:art direction 与格式回退。