图片格式选型:WebP、AVIF 与兼容回退

图片通常占网页体积的大头。换个格式,传输量可能直接减半,这是投入产出比最高的优化之一。

一、格式对比

格式压缩透明动画浏览器支持适用
JPEG有损全部照片
PNG无损全部截图、图标
GIF无损(256色)全部基本淘汰,用视频代替
WebP有损/无损现代浏览器全部通用首选
AVIF更好较新浏览器追求极致体积
SVG矢量全部图标、图形

实际压缩率(同一张照片,视觉质量相近)

bash
原图 JPEG   100%  (基准)
WebP       ~65%  (省 35%)
AVIF       ~45%  (省 55%)

2026 年,WebP 已经是默认选择(所有现代浏览器都支持)。AVIF 压缩更好但编码慢很多。

二、转换命令

bash
# 安装工具
sudo apt install -y webp imagemagick

# JPEG → WebP(质量 80 是常用甜点值)
cwebp -q 80 photo.jpg -o photo.webp

# 无损模式(截图、含文字的图)
cwebp -lossless screenshot.png -o screenshot.webp

# 批量
for f in *.jpg; do cwebp -q 80 "$f" -o "${f%.jpg}.webp"; done

# PNG → WebP
for f in *.png; do cwebp -lossless "$f" -o "${f%.png}.webp"; done

# 反向(WebP → PNG,偶尔要用)
dwebp photo.webp -o photo.png

AVIF:

bash
sudo apt install -y libavif-bin

# 编码(速度慢,quality 用 50-63)
avifenc -q 60 photo.jpg photo.avif

# 更快但压缩稍差
avifenc --speed 8 -q 55 photo.jpg photo.avif

用 ImageMagick 也能转:

bash
magick input.jpg -quality 80 output.webp
magick input.jpg -resize 1200x output.webp     # 同时缩放

三、兼容性回退:picture 元素

不想放弃老浏览器,用 <picture> 提供多格式:

html
<picture>
  <source srcset="photo.avif" type="image/avif">
  <source srcset="photo.webp" type="image/webp">
  <img src="photo.jpg" alt="照片" width="1200" height="800" loading="lazy">
</picture>

浏览器从上往下挑第一个能用的:支持 AVIF 就用 AVIF,否则 WebP,再不行 JPEG。

<img>必需的兜底,不能省。它是真正的显示元素,<source> 只是候选。

四、Nginx 内容协商(自动选格式)

不想改 HTML,可以让服务器根据 Accept 头自动返回:

nginx
http {
    map $http_accept $webp_suffix {
        default   "";
        "~*webp"  ".webp";
    }
}

server {
    location ~* ^(.+\.(?:png|jpe?g))$ {
        add_header Vary Accept;
        try_files $1$webp_suffix $uri =404;
    }
}

访问 photo.jpg 时:支持 WebP 的浏览器拿到 photo.jpg.webp,不支持的拿到原图。

add_header Vary Accept 必须有,否则 CDN 会缓存错版本。

五、响应式尺寸:srcset

同一个格式,不同屏幕给不同尺寸:

html
<img
  src="photo-800.webp"
  srcset="photo-480.webp 480w, photo-800.webp 800w, photo-1600.webp 1600w"
  sizes="(max-width: 700px) 100vw, 800px"
  alt="照片" width="800" height="533" loading="lazy">
属性作用
srcset + w提供候选及其真实宽度
sizes告诉浏览器这张图会显示多大
浏览器根据 DPR 和 sizes 自己挑

生成多尺寸:

bash
for w in 480 800 1200 1600; do
  cwebp -q 80 -resize $w 0 photo.jpg -o photo-$w.webp
done

六、尺寸与懒加载

无论什么格式,两个属性必写

html
<img src="a.webp" width="800" height="533" loading="lazy" alt="描述">
  • width/height:预留空间,防止 CLS(布局跳动);
  • loading="lazy":视口外的图延迟加载;
  • 首屏的图不要加 lazy(会拖慢 LCP),改用 fetchpriority="high"
html
<!-- 首屏关键图 -->
<img src="hero.webp" width="1600" height="600" fetchpriority="high" alt="...">

七、我的做法

本站是纯文字 + CSS 图形,基本没有图片,所以省掉了这一整块复杂度。这是刻意的选择:

  • 少一类资源 = 少一个故障点;
  • 没有图片 = 没有 CLS、没有 LCP 图片延迟;
  • 装饰用 CSS 渐变和 SVG,体积可以忽略。

如果要用图,我的流程是:

bash
1. 原始图缩到最大显示尺寸的 2 倍(比如显示 800px 就出 1600px)
2. 转 WebP(-q 80),关键图再出一份 AVIF
3. 写 width/height,非首屏加 loading="lazy"
4. 用 picture 做回退(或让 Nginx 协商)

八、检查与验证

bash
# 看站点图片总大小
curl -s https://www.bzii.cn | grep -oE "src=\"[^\"]+\.(jpg|png|webp|avif)" | sort -u

# 看单张图的实际传输大小
curl -sI https://www.bzii.cn/a.webp | grep -i "content-length"

# 确认 Content-Type 正确(浏览器靠它识别)
curl -sI https://www.bzii.cn/a.webp | grep -i "content-type"
# 应该是 image/webp

Nginx 里确认类型已注册:

bash
grep -i webp /etc/nginx/mime.types
# image/webp  webp;

九、常见错误

问题后果
只放 WebP 不给回退老浏览器显示不出来
没写 width/height布局跳动(CLS 差)
首屏图用了 lazyLCP 变慢
CDN 缓存没加 Vary不同浏览器拿到错的格式
原图直接上传(3000px 照片)体积爆炸

图片优化的顺序:先缩尺寸,再换格式,最后做响应式。 缩尺寸的收益往往比换格式更大。