picture 标签本身不优化图片,仅将选择权交给浏览器;写错 media 括号、type 大小写或 sizes 单位会导致所有 source 被跳过,只加载兜底 img。

直接说结论:picture 标签本身不“优化”图片,它只负责把选择权交给浏览器——你写错一个 media 括号、type 大小写不对、sizes 漏单位,浏览器就跳过所有 <source>,只加载 <img> 那张兜底图。
为什么电商产品图必须用 picture 而不是 img + srcset
商品主图常需同时满足三个不可妥协的条件:艺术裁剪(手机只留人脸,桌面展全景)、格式降级(WebP 优先,JPEG 回退)、DPR 适配(1x/2x/3x)。<img> 的 srcset 只能处理尺寸或像素密度,无法做艺术方向切换;而 <picture> 允许你为同一语义内容提供完全不同的图像源。
常见错误是把 <picture> 当成“高级 <img>”来用——比如只放一个 <source> 还加 type="image/webp",结果老浏览器直接空白(因为没兜底 <img>),新浏览器也因 MIME 不匹配被 Network 面板标为 blocked:mime-type。
media 断点必须和真实 CSS 媒体查询严格对齐
电商卡片宽度由 CSS 控制,<picture> 的 media 必须镜像它,否则断点缝隙会导致某像素宽度下所有 <source> 都不匹配,强制 fallback。
立即学习“前端免费学习笔记(深入)”;
- 你的 CSS 是
@media (max-width: 480px) { .card { width: 100%; } }→<source media="(max-width: 480px)">✅ - 写成
media="(max-width: 479px)"❌(480px 宽度时无匹配) - 写成
media="max-width: 480px"❌(缺括号,整个<source>被忽略) - 多个断点重叠如
(max-width: 768px)和(min-width: 768px)→ 768px 行为未定义,推荐用开区间:(max-width: 767px)→(min-width: 768px)→(min-width: 1201px)
sizes 属性写错等于白配 srcset
sizes 不是“建议尺寸”,而是告诉浏览器:“这张图在不同视口下**实际占多少 CSS 像素宽**”。浏览器用这个值 + srcset 里的 w 描述符,算出该拉哪张图。写错就会浪费带宽甚至模糊拉伸。
- 你的商品网格是
grid-template-columns: repeat(3, 1fr)→ 大屏下图宽 ≈33.333vw,sizes就得写"(max-width: 480px) 100vw, (max-width: 768px) 50vw, 33.333vw"✅ - 写成
"100%"或"300px"❌(浏览器无视) - 漏掉
sizes→ 浏览器默认按100vw算,哪怕图只占 300px 宽,也可能拉一张 1920w 的图下来 - Network 面板里某张 400w 图根本没发请求?说明
sizes算出的“显示宽度”远大于 400px,它被跳过了
WebP + JPEG 双格式回退必须顺序+类型+路径三重一致
浏览器按 <source> 从上到下匹配:第一个格式支持 + 媒体查询匹配的就加载,不再往下看。所以 WebP 必须放 JPEG 前面,且每组 srcset 的路径、type、服务器返回的 Content-Type 必须严丝合缝。
-
<source type="image/webp" srcset="prod-800.webp 1x, prod-1600.webp 2x">→ Nginx 必须返回Content-Type: image/webp,拼写、大小写、空格都不能错 ✅ - 写成
type="image/WebP"或服务器返回text/plain❌(Chrome 标为 blocked) - WebP
<source>放 JPEG 后面 → 不支持 WebP 的浏览器永远加载不到 JPEG,因为前面的 WebP 已匹配媒体查询但格式不支持,浏览器不会自动降级到下一个 - 每个
<source>的srcset里只能用w或x中的一种:混写"prod-400w.jpg 400w, prod@2x.jpg 2x"是未定义行为,Safari 可能整条跳过
真正容易被忽略的点是:CDN 缓存策略。静态图必须带内容哈希(如 /prod-abc123-800w.webp),否则更新图片后用户可能长期看到旧缓存——这不是 <picture> 的问题,但会让所有优化失效。



















