Skip to content

响应式图片

基于 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 告诉浏览器这张图在页面上会占多宽

html
<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 等任意长度值,但不能用百分比%);
  • 最后一项必须是「无媒体条件的默认值」;
  • 浏览器只认第一个匹配为真的条件,所以条件的书写顺序很重要,从窄到宽排好。

浏览器到底怎么挑

拿到上面这段,浏览器会:

  1. 评估屏幕尺寸、像素密度、缩放、方向、网络速度;
  2. 找出 sizes第一个为真的媒体条件,得到「这张图要占多宽」(槽位宽度);
  3. 把槽位宽度 ×设备像素比,算出「理想图宽」;
  4. srcset 里挑最接近、且通常不小于理想图宽的那张下载;找不到精确匹配时,挑「比理想宽度大的第一张再缩小适配」。

举例:480px 视口(≤600px → 槽位 100vw = 480px),普通屏选 photo-480.jpg;1000px 视口(→ 槽位 50vw = 500px),普通屏会选 photo-800.jpg(比 500 大的第一张)。手机因此不会白下 1200px 大图。

分辨率切换(二):像素密度描述符

当图片的显示尺寸是固定的(比如一个固定 320px 宽的头像),只是想在视网膜屏上更清晰,用密度描述符更简洁——不需要 sizes

html
<img
  srcset="avatar-320.jpg, avatar-480.jpg 1.5x, avatar-640.jpg 2x"
  src="avatar-640.jpg"
  alt="用户头像"
  width="320"
  height="320" />

配合 CSS 把显示宽度定死:

css
img {
  width: 320px;
}

工作方式:

  • 第一项不写描述符,隐含 1x(标准密度屏);
  • 2x 表示「2 设备像素对 1 CSS 像素」的高 DPI 屏用这张;
  • 浏览器测出当前屏幕密度,下对应那张——普通屏下 avatar-320.jpg,2× 屏下 avatar-640.jpg

wx 怎么选

  • 图的显示宽度会随布局变化(响应式占位)→ 用 w 描述符 + sizes
  • 图的显示尺寸固定、只想换清晰度(头像、Logo、固定尺寸缩略图)→ 用 x 描述符;
  • 两者绝不混写在同一个 srcset 里。优先考虑 w + sizes,因为它更灵活,且省流量模式下浏览器能自行降级。

src 为什么还要留着

html
<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 与格式回退