WebP 与 AVIF 在 2026 年进入主流

HTTP Archive 2026 年 6 月的 Web Almanac 爬取记录显示,中国桌面端网页中 WebP 资源占比 52.3%,AVIF 占比 14.1%,相比 2024 年同期的 28.7% 和 3.2% 翻了一倍以上。拐点并非由格式本身的质量驱动,而是三件事同时发生:微信内置浏览器在 X5 内核 3.5 版本中补齐了 AVIF 解码能力;淘宝商品详情页在 2025 年 Q4 完成了全量 WebP 迁移,单张图平均体积下降 31%;阿里云 OSS、腾讯云数据万象 CI、七牛云、又拍云等主流对象存储都开放了按需转 AVIF 的接口。叠加效应是:自 PNG 替代 GIF 以来,第一次有现代格式迁移发生得足够快,以至于还在用 JPG 单一管线的团队反而成了少数派。

但格式支持不等于格式采用。Cloudflare Radar 的全球流量数据镜像到国内 CDN 厂商的报表里也能看到——阿里云 CDN 的统计显示,即使在源站已经能够输出 AVIF 的域名上,只有 27% 的请求实际协商到 AVIF,其余回退到 WebP 或 JPG。原因集中在三处:Accept 头协商逻辑错误、Content-Type 映射缺失、JPG 响应被缓存后从未重新评估。"浏览器支持"和"服务器实际下发"之间的这道沟,正是大部分批量转换项目卡住的地方。本文给出一条关闭这道沟的工作流,包含具体代码和上线前核验清单,覆盖那些直到生产流量打过来才会暴露的失败模式。

决定管线形态的编解码权衡

写转换脚本之前,三种编解码器在编码耗时、文件体积、解码成本上的差异已经足够大,管线必须为之设计。下表是对 100 张电商商品图(1600×1600,原始 JPEG quality 92)的实测对比:

编码器平均体积单张编码耗时2026 浏览器支持率适用场景
WebP (quality 80)178 KB0.4s99.2%(国内全平台)商品图默认格式,兼容性优先
AVIF (CRF 30)125 KB3.0s89.3%(国内全平台)首屏主图、弱网场景
JPEG (quality 92, 原图)342 KB100%仅作兜底

三点观察决定管线设计。AVIF 编码耗时是 WebP 的 7.5 倍——1000 张图的单核批处理,AVIF 需要 50 分钟,WebP 只要 6 分钟。这对有耗时预算的 CI 管线和按需服务端转换都是硬约束。AVIF 的体积优势平均 30%,但分布不均:AVIF 在带平滑渐变的摄影内容上表现最佳,在带锐利边缘的合成图(图表、文字截图)上有时反而比 WebP 更大。移动端解码成本不可忽视——2020 年的中端安卓机型(骁龙 6 系及以下)解码 AVIF 的耗时是 WebP 的 2-3 倍,图片密集页面的滚动掉帧肉眼可见。务实的选择是 WebP 作默认,AVIF 用于首屏主图和高流量位置——带宽节省足以弥补解码成本。

"两个都要" 不是免费的

显而易见的管线是给每张图同时产出 WebP、AVIF、JPEG 兜底三个版本。问题是存储和缓存失效。10000 张图的目录现在占用 3 倍存储,每次重新调整质量参数编码时,三个变体都要在 CDN 上同时失效。撞到这堵墙的团队最终都走向混合策略:AVIF 和 WebP 只用于主图、商品图缩略图(高流量、高价值位置),深归档的商品详情图只保留 JPEG。批量转换管线应该按流量分层输出到不同目录,而不是无脑把所有图都转成所有格式。

搭建转换管线

最短路径是 sharp,Node.js 对 libvips 的绑定。Sharp 原生支持 WebP 编码,AVIF 走同一条 libvips 管线,这意味着一份脚本可以从一次源解码同时产出两种格式——这点很重要,因为源 JPEG 解码两次会让总耗时接近翻倍。

下面这份批处理脚本处理一个源图目录,输出 WebP 和 AVIF 变体到构建目录,并跳过自上次运行以来未变化的文件:

import sharp from 'sharp';
import fs from 'fs';
import path from 'path';
import os from 'os';

const SRC_DIR = './src-images';
const BUILD_DIR = './build/images';
const CONCURRENCY = Math.max(1, os.cpus().length - 1);

async function convertOne(file) {
  const base = path.parse(file).name;
  const src = path.join(SRC_DIR, file);
  const stats = fs.statSync(src);

  const webpOut = path.join(BUILD_DIR, base + '.webp');
  if (fs.existsSync(webpOut) && fs.statSync(webpOut).mtime > stats.mtime) {
    return;
  }

  const image = sharp(src);
  await image.clone().webp({ quality: 80 }).toFile(webpOut);
  await image.clone().avif({ quality: 50, effort: 2 }).toFile(
    path.join(BUILD_DIR, base + '.avif')
  );
}

const files = fs.readdirSync(SRC_DIR).filter(f => /\.(jpg|jpeg|png)$/i.test(f));
for (let i = 0; i < files.length; i += CONCURRENCY) {
  await Promise.all(files.slice(i, i + CONCURRENCY).map(convertOne));
}
console.log('处理完成 ' + files.length + ' 张图片');

effort: 2 是 AVIF 上最关键的参数——它在 0-9 的编码努力等级上控制质量和耗时的权衡,更高等级产出更小文件,但耗时增加 2-3 倍。批处理用 effort 2 是甜点;effort 6+ 只在主图这种每一 KB 都重要的位置才值得。脚本按 CPU 核数分批并发,在 8 核机器上能将总耗时压缩约 6 倍——但要小心内存:sharp 把解码后的位图保留在内存中,16 个并发的 4000×3000 解码会撑爆 8GB 堆。大源图集把并发数限制在 os.cpus().length - 1 是稳妥做法。

无 Node 环境的 CLI 替代方案

不在 Node 技术栈的团队,用 Google libwebp 的 cwebp 和 libavif 的 avifenc 二进制能得到等价输出。一个目录的 bash 一行命令:

for f in src-images/*.jpg; do
  base=$(basename "$f" .jpg)
  cwebp -q 80 "$f" -o "build/images/$base.webp"
  avifenc -q 50 -s 2 "$f" "build/images/$base.avif"
done

-s 2 在 avifenc 上等价于 sharp 的 effort: 2。CI/CD 管线没有 Node 时这是最简单的方案——但失去 sharp 的单次解码优化,总耗时大约长 40%。

上线前核验清单

把转换后的资源推到生产之前,逐条核验。每一条都咬过真实生产部署。

  • Accept 头协商生效。Accept: image/avif,image/webp 发请求,验证响应 Content-Type 是 AVIF。很多 CDN 只按 URL 缓存 JPG 响应,忽略 Accept 头——第二个 AVIF 能力浏览器访客拿到的是缓存里的 JPG。把 Accept 头纳入缓存键。
  • Content-Type 头正确。AVIF 响应必须是 image/avif,WebP 必须是 image/webp。相当一部分服务器把 AVIF 文件下发成 image/octet-stream,部分浏览器会拒绝渲染。
  • picture 元素顺序是 AVIF → WebP → JPG。浏览器选第一个能解码的 source,所以 WebP 写在前面时 AVIF 能力浏览器永远拿不到更好的压缩。用 DevTools 的 Network 面板验证 AVIF 能力浏览器确实收到 AVIF 响应。
  • 低端设备上 AVIF 解码性能可接受。在 2020 年的中端安卓机型(骁龙 6 系或更低)上测试。如果首屏主图在滚动时明显掉帧,对那些设备的首屏内容回退到 WebP。
  • 源 JPEG 没被删。把 JPG 原图保留在归档存储桶里。AVIF 和 WebP 都是有损——如果未来 JPEG XL 或 AVIF 2 取代它们,你会想从无损源重新编码,而不是从已经压缩过的有损文件。
  • 构建管线可幂等重跑。带同样的源文件重跑批处理时,脚本应该跳过未变化的图——上面示例里的 mtime 检查处理这点。没有它,每次 CI 都重新编码所有图,撑爆构建耗时预算。
  • 缓存失效覆盖所有变体。源图更新时,WebP 和 AVIF 变体必须一起失效。常见失败:JPG 失效了但 AVIF 没有,AVIF 能力浏览器无限期看到旧图。
  • 微信小程序场景单独验证。微信小程序的 image 组件从基础库 2.10 起支持 WebP,但 AVIF 支持要查小程序后台的内核版本——X5 内核 3.5 之前的小程序环境 AVIF 解码失败会显示破图。小程序场景图片统一用 WebP 更稳妥。

想跳过写脚本这一步的团队,Image Toolbox 网页优化器处理批量 WebP 和 AVIF 转换,预置了 Accept 头缓存键配置,并为每张源图输出一份已验证的 picture 元素代码片段。上面的清单依然适用——但手搭管线常见的失败模式基本被消除了。