<picture>不能只靠src生效,因为它是容器而非渲染元素;必须嵌套<source>(按media/type顺序匹配)和末尾带src的<img>作为强制fallback,缺一不可。

为什么 <picture> 不能只靠 src 属性生效
因为 <picture> 本身不加载图片,它只是容器;真正起作用的是内部的 <source> 和末尾的 <img>。浏览器会按顺序检查每个 <source> 的 media 或 type,匹配成功就用它的 srcset(或 src),跳过后续;都不匹配时才回退到 <img> 的 src。
常见错误是漏掉 <img> 标签,或把它写成自闭合(<img />)——这会导致整个 <picture> 不渲染图片。
-
<source>必须在<img>之前,且不能有src属性(它只认srcset+sizes或media) -
media值必须是合法媒体查询,比如(min-width: 768px),不能写成768px或tablet -
<img>必须有src和alt,否则可访问性失败,且部分浏览器(如旧版 Safari)可能不触发加载
怎么用 srcset + sizes 实现宽度适配
当设备像素比(dpr)和视口宽度都变化时,仅靠 media 切换固定图不够精细。srcset 配合 sizes 能让浏览器根据当前布局宽度 + dpr 自动选最接近的资源。
例如:一个占满视口宽度的 banner 图,在小屏用 375w 版本,中屏用 768w,大屏用 1200w,同时支持 2x 屏:
<picture>
<source
media="(min-width: 1200px)"
srcset="banner-1200w.jpg 1x, banner-2400w.jpg 2x">
<source
media="(min-width: 768px)"
srcset="banner-768w.jpg 1x, banner-1536w.jpg 2x">
<source
media="(min-width: 375px)"
srcset="banner-375w.jpg 1x, banner-750w.jpg 2x">
<img
src="banner-fallback.jpg"
alt="首页横幅"
sizes="(min-width: 1200px) 1200px, (min-width: 768px) 768px, 100vw">
</picture>
-
sizes告诉浏览器“这张图在不同断点下会占用多宽”,不是“应该加载多宽的图” -
srcset中的1x/2x是设备像素比提示,不是文件名规则;浏览器会结合sizes计算出需要的物理像素宽度,再选最匹配的源 - 如果省略
sizes,浏览器默认按100vw算,可能导致小屏加载大图
遇到 HTTP 404 但路径明明存在?检查 MIME 类型和服务器配置
有些 CDN 或静态服务(如 GitHub Pages、Vercel 默认配置)对 .webp 或 .avif 返回 text/plain,导致浏览器拒绝加载,控制台报 Failed to load resource: the server responded with a status of 404 () —— 实际是 MIME 错误,不是真 404。
- 用
curl -I your-image.webp查响应头,确认Content-Type是image/webp - Nginx 需添加:
types { image/webp webp; image/avif avif; } - Apache 需在
.htaccess加:AddType image/webp .webp - 本地开发用
serve或http-server时,它们默认不支持.webpMIME,建议换vite preview或加代理
要不要用 <picture> 替代所有 <img>?看场景
不是所有图都需要 <picture>。它适合三类明确需求:艺术方向切换(如横图/竖图)、格式降级(webp → jpg)、关键尺寸适配(如 banner 占满视口)。普通头像、图标、内嵌小图用 <img srcset> 就够了。
- SEO 友好性:搜索引擎目前仍主要抓取
<img>的src,所以<picture>的 fallbacksrc应该是语义最完整的版本 - 性能代价:每个
<source>都是一次独立资源发现,过多<source>可能拖慢首帧(尤其网络差时),建议单个<picture>不超过 4 个<source> - 兼容性底线:IE 完全不支持
<picture>,如需支持,得用picturefillpolyfill 或服务端 UA 判断降级
最易被忽略的是 sizes 的动态性——它写死在 HTML 里,如果图宽由 JS 控制(比如 slider 组件),就得用 ResizeObserver 动态改 img.sizes,否则浏览器始终按初始值选图。

















