图片格式选型:WebP、AVIF 与兼容回退
图片通常占网页体积的大头。换个格式,传输量可能直接减半,这是投入产出比最高的优化之一。
一、格式对比
| 格式 | 压缩 | 透明 | 动画 | 浏览器支持 | 适用 |
|---|---|---|---|---|---|
| JPEG | 有损 | ❌ | ❌ | 全部 | 照片 |
| PNG | 无损 | ✅ | ❌ | 全部 | 截图、图标 |
| GIF | 无损(256色) | ✅ | ✅ | 全部 | 基本淘汰,用视频代替 |
| WebP | 有损/无损 | ✅ | ✅ | 现代浏览器全部 | 通用首选 |
| AVIF | 更好 | ✅ | ✅ | 较新浏览器 | 追求极致体积 |
| SVG | 矢量 | ✅ | ✅ | 全部 | 图标、图形 |
实际压缩率(同一张照片,视觉质量相近)
原图 JPEG 100% (基准)
WebP ~65% (省 35%)
AVIF ~45% (省 55%)2026 年,WebP 已经是默认选择(所有现代浏览器都支持)。AVIF 压缩更好但编码慢很多。
二、转换命令
# 安装工具
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.pngAVIF:
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 也能转:
magick input.jpg -quality 80 output.webp
magick input.jpg -resize 1200x output.webp # 同时缩放三、兼容性回退:picture 元素
不想放弃老浏览器,用 <picture> 提供多格式:
<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 头自动返回:
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
同一个格式,不同屏幕给不同尺寸:
<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 自己挑 |
生成多尺寸:
for w in 480 800 1200 1600; do
cwebp -q 80 -resize $w 0 photo.jpg -o photo-$w.webp
done六、尺寸与懒加载
无论什么格式,两个属性必写:
<img src="a.webp" width="800" height="533" loading="lazy" alt="描述">width/height:预留空间,防止 CLS(布局跳动);loading="lazy":视口外的图延迟加载;- 首屏的图不要加 lazy(会拖慢 LCP),改用
fetchpriority="high"。
<!-- 首屏关键图 -->
<img src="hero.webp" width="1600" height="600" fetchpriority="high" alt="...">七、我的做法
本站是纯文字 + CSS 图形,基本没有图片,所以省掉了这一整块复杂度。这是刻意的选择:
- 少一类资源 = 少一个故障点;
- 没有图片 = 没有 CLS、没有 LCP 图片延迟;
- 装饰用 CSS 渐变和 SVG,体积可以忽略。
如果要用图,我的流程是:
1. 原始图缩到最大显示尺寸的 2 倍(比如显示 800px 就出 1600px)
2. 转 WebP(-q 80),关键图再出一份 AVIF
3. 写 width/height,非首屏加 loading="lazy"
4. 用 picture 做回退(或让 Nginx 协商)八、检查与验证
# 看站点图片总大小
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/webpNginx 里确认类型已注册:
grep -i webp /etc/nginx/mime.types
# image/webp webp;九、常见错误
| 问题 | 后果 |
|---|---|
| 只放 WebP 不给回退 | 老浏览器显示不出来 |
| 没写 width/height | 布局跳动(CLS 差) |
| 首屏图用了 lazy | LCP 变慢 |
| CDN 缓存没加 Vary | 不同浏览器拿到错的格式 |
| 原图直接上传(3000px 照片) | 体积爆炸 |
图片优化的顺序:先缩尺寸,再换格式,最后做响应式。 缩尺寸的收益往往比换格式更大。
