先说结论:体积的锅,格式背不动
图片体积 ≈ 像素数量 × 每个像素携带的信息量。格式(JPEG / WebP / AVIF)决定的是后半段——编码器能把这点信息压到多小,它压不动你图里本来就不该存在的东西。一张 4032×3024 的图有 1220 万个像素,你只打算用 750px 宽显示,那其中 96% 的像素从头到尾没人看见,但每一个都在占字节。所以压缩这件事,第一步永远是删,不是编码。
我见过太多人卡在「选 JPEG 还是 WebP」上纠结三天,却从来没右键看过一眼图片属性里的尺寸。
上个月帮一个做跨境电商的朋友看详情页,主图打开要四秒多。他把原图发我,是一张 4032×3024 的 iPhone 原片,8.7MB。他说他已经压过一轮了,用某个在线工具,压完 1.8MB,「应该够小了吧」。我问他这张图在页面上打算显示多大,他说详情页主体宽度 750px。问题就在这——8.7MB 和 1.8MB,都是在为 4032 个像素宽的数据付费,而屏幕上只兑现 750 个。
下面是我实际跑的一遍,所有数字都是同一张原图测出来的,环境是 macOS + ImageMagick 7 + mozjpeg 4.1.1 + libwebp 1.3.2 + libavif 1.0。
每一步的实测体积
| 版本 | 尺寸 | 体积 | 备注 |
|---|---|---|---|
| 原图 HEIC | 4032×3024 | 8.7MB | iPhone 直出 |
| 转 PNG 无损 | 4032×3024 | 21.3MB | 别问,问就是无损 |
| mozjpeg q85,不缩尺寸 | 4032×3024 | 2.4MB | 只压了质量没缩尺寸 |
| mozjpeg q85 | 1500×1125 | 214KB | 缩到 2 倍图 |
| 加上 4:2:0 采样 | 1500×1125 | 178KB | 色度信息砍掉 3/4 |
| WebP q80 -m 6 | 1500×1125 | 112KB | |
| WebP q80 + sharp_yuv | 1500×1125 | 118KB | 边缘干净点,多花 6KB |
| AVIF q28 speed 4 | 1500×1125 | 89KB | 编码耗了 11 秒 |
从 8.7MB 到 214KB,靠的是缩尺寸,不是换格式。从 214KB 到 112KB,才是格式的功劳。顺序反了,就白忙。
对应命令大概是这些:
# 缩放 + 去元数据 + 4:2:0 采样
magick src.heic -resize 1500x -colorspace sRGB -strip \
-sampling-factor 4:2:0 -interlace Plane -quality 82 out.jpg
# mozjpeg 走一遍,tune-ms-ssim 比默认的 psnr 观感更好
cjpeg -quality 82 -tune-ms-ssim -sample 2x2 -optimize -outfile final.jpg out.jpg
# WebP
cwebp -q 80 -m 6 -sharp_yuv -metadata none -resize 1500 0 final.jpg -o final.webp

# AVIF
avifenc --min 20 --max 30 --speed 4 --yuv 420 --jobs 4 final.jpg final.avif
那个把体积翻倍的隐形杀手:锐化
这是我踩过最亏的一次坑。朋友的图在 Lightroom 里导出时「锐化」拉到 80,「细节」60,他说这样看起来才清楚。
JPEG 的编码核心是 8×8 像素块的 DCT 变换。一张干净的图,高频系数大多是 0 或接近 0,编码器几乎不用花码率;但锐化制造出来的边缘光晕和噪点,是彻头彻尾的高频信号,会把每个块的高频系数全部填满。结果就是:同样 1500×1125、同样 q85,他那张锐化过的图压出来 340KB,我自己重新导出一版没锐化的 178KB。差了将近一倍,肉眼在详情页上完全看不出区别。
如果原图已经锐化过了,补救办法是在压缩前做一次很轻的降噪,半径 0.3~0.5 就够:
magick in.jpg -gaussian-blur 0.4 -resize 1500x -quality 82 out.jpg
注意这个顺序——降噪必须在缩放之后、编码之前。先降噪再缩放,模糊会被放大回来。另外手机夜景模式拍的图天生噪点多,这一类图换成 AVIF 收益会明显大于普通图,因为 AVIF 的块内预测对随机噪声的容忍度确实比 JPEG 高一截。
关于色彩配置和 EXIF,有个坑得说清楚
-strip 这两个字很多人是无脑加的,包括以前的我。但如果你拍的是 Display P3 的图(新款 iPhone 默认就是这个色彩空间),直接 strip 掉 ICC 配置,浏览器会按 sRGB 解释那些数值,红橙色会整个闷下去,肤色发灰。
普通 sRGB 的 ICC 配置只有 3KB 左右,丢不丢无所谓;苹果设备导出的 P3 配置也就几 KB。正确做法是先转到 sRGB 再 strip,而不是直接删:
magick in.jpg -profile /System/Library/ColorSync/Profiles/sRGB Profile.icc -strip out.jpg
EXIF 倒是可以放心删,手机直出的 EXIF 一般 10~40KB,里面有 GPS 坐标、设备序列号、拍摄时间。除非你要做摄影作品集,否则这些信息对网页加载没有任何价值。
我对格式这件事的独立看法
现在的普遍说法是「AVIF 比 WebP 小 30%,WebP 比 JPEG 小 30%」,这个数据本身没错,但它有个前提:测试用的是原始的高质量图片。
一旦你按上面那套流程先把尺寸缩了、噪点降了,AVIF 相对 WebP 的优势会从 30% 掉到 8% 左右——我这次实测就是 89KB 对 112KB,差 23KB。为了这 23KB,你要付出的是:编码时间从 0.3 秒变成 11 秒(36 倍),构建流程里多一个原生依赖,以及给 2020 年前的老设备准备回退方案。
我的判断是:内容站、博客、后台系统,WebP 就够了,jpg + webp 双份输出,兼容性和收益平衡得最好。只有两类场景值得上 AVIF——一是图片量级在十万张以上、带宽成本真实存在的电商站;二是摄影、艺术类站点,图本身就是内容,那 8% 的观感提升是值得的。
还有个反直觉的点:AVIF 在极低码率(相当于 JPEG q40 以下)区间的优势非常夸张,能到 2~3 倍,但在中高码率区间反超幅度骤减。所以如果你的图本来就压得很狠,换 AVIF 收益最大;如果你的图是 q82 这种「看起来很干净」的档位,换格式省下的那点体积,还不如把尺寸从 1500px 降到 1200px 来得实在。
我现在的固定流程
- 先问显示尺寸。响应式页面就按断点三分:640 / 1200 / 1920,配 srcset。桌面端不搞 2x 图,那是给手机和 Retina 笔记本用的。
- 缩放。ImageMagick 默认的 Lanczos 就可以了,缩小的场景下它比 bicubic 锐一些,但也更容易出振铃,人像图我会换成
-filter Triangle。 - 看看原图是否锐化过。光线充足、光圈 f5.6 以上的图基本不用降噪;夜景、室内、手机长焦的图,加 0.4 半径。
- 转色彩空间到 sRGB,strip 掉 EXIF 和多余配置。
- 输出 JPEG q82 打底,再出一份 WebP q80。用
<picture>标签包起来,AVIF 暂时不加。 - 全部跑一遍
jpegoptim --strip-all --all-progressive --max=82,能再抠出 3%~5%。渐进式 JPEG 对首屏感知速度的提升其实比体积更明显。
最后说一句可能有点扫兴的话:图片优化这件事,80% 的收益来自「把尺寸改对」这一步,剩下的 20% 才轮得到格式、采样、渐进式这些技术细节去分。但大家总是更愿意研究后者,因为前者太朴素了,朴素到不像一个技术问题。
我刚开始做前端那两年也是这样,花一下午调 cwebp 的参数,就为了从 118KB 抠到 112KB,然后交上去一个 2400px 宽的 banner 图。