如何用 picture 和 source 实现响应式图片与 AVIF 回退
用 <picture> 包住多个 <source>,把 AVIF 放第一个、WebP 放第二个、JPEG 放进最后的 <img>,再配合 srcset + sizes 做分辨率适配,就是响应式图片 + AVIF 回退的标准做法——不支持 AVIF 的浏览器会根据 type 属性自动跳到下一个 <source>,全程零 JavaScript。
为什么 <picture> 能回退,而 <img srcset> 不能?
结论:<img> 的 srcset 只做「尺寸/分辨率选择」,它不感知图片格式;只有 <picture> 里的 <source> 才支持 type 属性做格式协商。
浏览器渲染 <picture> 时按顺序逐个检查 <source>:先看 type 声明的 MIME 类型自己支不支持,再看 media 媒体查询是否匹配,第一个全部通过的 <source> 会被选中,后面的直接忽略。如果所有 <source> 都不匹配,就落到最后的 <img>。这就是回退机制的全部原理,不需要任何特性检测脚本。
AVIF、WebP 的回退顺序为什么要从新到旧?
结论:回退链必须按「压缩率从高到低」排列,顺序写反会导致新格式永远不生效。
AVIF(AV1 Image File Format)在同等主观质量下,体积通常比 JPEG 小 40%~50%,比 WebP 再小约 20%。浏览器支持情况:Chrome 85(2020 年 8 月)、Firefox 93(2021 年 10 月)、Safari 16(2022 年 9 月)开始支持 AVIF;WebP 的支持面更广,Chrome 23、Firefox 65、Safari 14 就已覆盖。所以顺序是 AVIF → WebP → JPEG,越靠前越好,越靠后兜底越稳。
完整的代码怎么写?
<picture>
<source type="image/avif"
srcset="hero-480.avif 480w, hero-960.avif 960w, hero-1440.avif 1440w"
sizes="(max-width: 800px) 100vw, 50vw">
<source type="image/webp"
srcset="hero-480.webp 480w, hero-960.webp 960w, hero-1440.webp 1440w"
sizes="(max-width: 800px) 100vw, 50vw">
<img src="hero-960.jpg"
srcset="hero-480.jpg 480w, hero-960.jpg 960w, hero-1440.jpg 1440w"
sizes="(max-width: 800px) 100vw, 50vw"
width="1440" height="960"
alt="示例配图" loading="lazy" decoding="async">
</picture>
几个硬性要求:<img> 必须存在且必须有 src,它是最终兜底;loading="lazy"、decoding="async"、width/height 都写在 <img> 上,写到 <source> 上无效;sizes 最后一项必须是无媒体条件的默认值(如 50vw),否则整条 sizes 会被判为无效。
sizes 写错会浪费多少流量?
结论:sizes 描述的是图片在页面上的实际渲染宽度,写错会让浏览器选错文件,典型后果是手机下载 1440px 大图。
写法是「媒体条件 + 宽度」,例如两栏布局:sizes="(min-width: 800px) 50vw, 100vw";三栏卡片:sizes="(min-width: 1000px) 33vw, (min-width: 600px) 50vw, 100vw"。浏览器用 sizes 推出的 CSS 像素宽度 × 设备像素比,去 srcset 里挑最接近的候选图。如果你在 375px 宽的手机上误写成 100vw,而 CSS 里图片其实只有半屏宽,就会多下载约一倍的字节。
用命令行批量生成 AVIF 和 WebP 怎么做?
结论:推荐 avifenc 和 cwebp 两个命令行工具,质量参数起步值分别是 AVIF 50~60、WebP 75~80,再按肉眼效果上下微调。
avifenc -q 60 hero-960.png hero-960.avif
# WebP:质量 80,同时缩放到宽 960
cwebp -q 80 -resize 960 0 hero.jpg -o hero-960.webp
如果用 Node,sharp 更方便:sharp('hero.jpg').resize(960).avif({ quality: 55 }).toFile('hero-960.avif')。同时要在服务器上确认 .avif 返回 Content-Type: image/avif,.webp 返回 image/webp,否则部分浏览器会拒绝解码。
最容易踩的三个坑是什么?
结论:type 与实际文件格式不一致时,浏览器不会自动回退,直接破图——这是最贵的一课。
第一,type="image/avif" 却指向了一个 JPEG 文件,浏览器认为这是「支持的格式」,尝试解码失败后不会跳到下一个 <source>,而是显示裂图。第二,把 <img> 放在 <picture> 里的非末尾位置,规范要求 <img> 必须是最后一个子元素。第三,想用 <picture> 做 CSS background-image 的格式回退——那是不生效的,背景图必须用 image-set()。
怎么验证回退真的在跑?
结论:在 DevTools 的 Network 面板按图片类型筛选,看实际请求的是 .avif 还是 .jpg。
Chrome 中打开 Network → Img 筛选,刷新页面,正常情况下应看到 .avif 请求;想验证回退,用 Firefox 92 或 Safari 15 这类不支持 AVIF 的版本打开同一页面,应看到 .webp;再老的浏览器应看到 .jpg。也可以用 Lighthouse 的「Serve images in next-gen formats」审计确认没有遗漏的未转换图片。
总结:<picture> + type 是格式回退的唯一正解,顺序按 AVIF → WebP → JPEG 排,srcset 用 w 描述符配 sizes 做尺寸适配,<img> 保留 src、alt、width/height 兜底;生成端用 avifenc/cwebp 或 sharp 批量产出整套尺寸,再确认服务器 MIME 正确。这套组合在 Chrome 85+、Firefox 93+、Safari 16+ 上都会优先加载 AVIF,老浏览器自动降级,不需要写一行检测代码。
原文链接:https://www.gj0.com/thread-768.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。