4、图片压缩:有损压缩(JPEG/WebP/AVIF)、无损压缩(PNG/PNGQuant)、SVG压缩与雪碧图

图片,往往是接口响应体里的「重量级选手」。一张未经处理的截图动辄几 MB,直接拖垮首屏加载。我见过太多团队在接口层面做各种花式缓存,却忽略了图片本身就没优化过——这其实是在用缓存掩盖设计缺陷。

这一节,我们聚焦图片压缩的四种核心手段。说白了,就是让你知道什么场景该用有损、什么场景必须无损、SVG 怎么瘦身、以及雪碧图到底还有没有用。

核心原则:图片压缩不是「压得越小越好」,而是在视觉可接受的前提下,找到体积与质量的最佳平衡点。
图片压缩技术体系 图片压缩 有损压缩 JPEG / WebP / AVIF 无损压缩 PNG / PNGQuant SVG 压缩 SVGO / 路径精简 雪碧图 CSS Sprites 应用场景:接口响应体 / 移动端适配 / 首屏优化 / 带宽敏感环境

4.1 有损压缩:JPEG、WebP、AVIF

有损压缩,就是主动丢弃一些人眼不敏感的色彩或细节信息,来换取更小的体积。你想想看,一张照片里相邻的几百个像素颜色几乎一样,没必要每个都精确记录。

JPEG

老牌选手,兼容性最好。几乎所有设备都支持。但 JPEG 有个硬伤——不支持透明通道。而且它在低码率下容易出现「块效应」,就是那种 8x8 格子分明的马赛克感。

我个人的习惯是:照片类图片用 JPEG,质量参数设在 75-85 之间。低于 70 时画质下降明显,高于 90 时体积增长快但收益递减。

经验值:一张 1920x1080 的 JPEG 照片,质量 80 时大约 200-300KB,质量 95 时可能飙到 1MB 以上。肉眼差异?说实话,在普通屏幕上几乎看不出来。

WebP

Google 推出的格式,同时支持有损和无损。有损 WebP 比同质量的 JPEG 小 25%-35%。我曾在某个资讯类项目中,把列表页的缩略图全部换成 WebP,接口返回体从 1.2MB 降到了 780KB,首屏速度提升了近 40%。

但要注意:WebP 的解码耗时比 JPEG 高。在低端 Android 设备上,大量 WebP 图片同时解码可能导致 UI 线程卡顿。我的建议是——服务端做格式协商,根据 User-Agent 决定返回 WebP 还是 JPEG。

AVIF

基于 AV1 编码的新格式。压缩率比 WebP 再高 20%-30%,而且支持 HDR 和广色域。但兼容性目前还是短板——iOS 16 之后才原生支持,部分老浏览器完全不认识。

我曾经在内部工具项目里试过 AVIF,效果确实惊艳。一张 2MB 的 PNG 截图,转成 AVIF 后只有 120KB,而且肉眼几乎看不出区别。但生产环境我暂时不敢全面铺开,目前更适合做「渐进增强」——支持 AVIF 的浏览器用 AVIF,不支持的降级到 WebP 或 JPEG。

格式 压缩率(相对 JPEG) 透明通道 浏览器兼容 解码性能
JPEG 基准 不支持 100% 极快
WebP(有损) 小 25%-35% 支持 ~95% 中等
AVIF 小 50%-60% 支持 ~70% 较慢
避坑:不要对「文字截图」使用有损压缩。JPEG 和 WebP 的有损模式会让文字边缘出现模糊和伪影,严重影响可读性。文字类图片请用无损格式。

4.2 无损压缩:PNG 与 PNGQuant

无损压缩,就是不丢失任何像素信息。PNG 是典型代表。但 PNG 的压缩算法其实分两步:先做「过滤」(预测像素差异),再做「Deflate 压缩」(类似 ZIP)。

很多开发者直接拿 Photoshop 导出 PNG,体积大得离谱。其实 PNG 的压缩率很大程度上取决于你用什么工具。

PNGQuant

这是一个能把 PNG 体积压到极致的工具。它的核心思路是:减少颜色数量。比如一张 32 位 PNG 有 1600 万色,但实际可能只用到了 200 种颜色。PNGQuant 会把颜色量化到 256 色甚至更少,然后重新索引。

我记得有一次,设计给了一张 800KB 的 PNG 图标,我用 PNGQuant 跑了一下:

pngquant --quality=65-80 --speed=1 --output=compressed.png input.png

结果输出只有 120KB,肉眼完全看不出区别。为什么?因为图标本身颜色就少,量化后几乎无损。

使用建议:对于 UI 切图、图标、插画类 PNG,先用 PNGQuant 压一遍。质量参数建议 60-80,低于 60 时可能出现色带。速度参数用 1(最慢但质量最好),CI 构建时可以用 3 或 4 加快速度。

另外,PNGQuant 支持生成 8 位 PNG 和 RGBA 半透明。这一点比 GIF 强太多。我见过有人为了透明效果用 GIF,结果体积大、颜色差——其实 PNGQuant 压过的 8 位 PNG 才是正解。

4.3 SVG 压缩

SVG 是矢量图,理论上可以无限缩放。但很多设计师导出的 SVG 文件里,充满了冗余信息:空的 <g> 标签、无用的元数据、编辑器自带的命名空间、小数点后 8 位的坐标值……

我习惯用 SVGO(SVG Optimizer)来做自动化清理。它就像一个代码压缩器,但针对的是 SVG 语法。

npx svgo icon.svg -o icon.min.svg

一个典型的优化效果:

优化项 原始 优化后
文件大小 12.5 KB 3.2 KB
路径精度 小数点后 6 位 小数点后 2 位
冗余标签 17 个 0 个
元数据 包含 移除

我曾经接手过一个项目,首页用了 40 多个 SVG 图标,总大小 480KB。用 SVGO 批量处理一遍后,降到了 110KB。而且因为是构建阶段自动执行的,后续开发完全无感知。

注意:SVGO 的某些插件可能会破坏动画或交互。比如 removeViewBox 插件会删掉 viewBox 属性,导致缩放失效。我的做法是:在 CI 配置里显式禁用不安全的插件,只开启清理类插件。

4.4 雪碧图(CSS Sprites)

雪碧图,就是把多张小图拼成一张大图,通过 CSS 的 background-position 来定位显示哪一块。它的核心价值是减少 HTTP 请求数。

在 HTTP/1.1 时代,每个图片请求都要建立 TCP 连接,浏览器对同一域名还有并发限制(通常是 6 个)。雪碧图能把几十个图标请求合并成一个,效果立竿见影。

但到了 HTTP/2 时代,多路复用让单个连接的并发能力大幅提升。雪碧图的优势被削弱了。我个人现在的判断是:

  • 小图标(< 2KB):用雪碧图仍然有价值,因为合并后可以减少请求头开销
  • 中等图标(2-10KB):看情况。如果图标数量多且变化少,雪碧图依然有效
  • 大图或频繁变动的图:不要用雪碧图。每次改一个图标都要重新生成整张图,缓存失效成本太高

我记得有个电商项目,运营每周都要换促销图标。一开始用了雪碧图,每次更新都要重新拼图、重新部署。后来改成独立 SVG 图标 + HTTP/2 多路复用,反而更灵活,加载速度也没差多少。

现代替代方案:对于图标类资源,我更推荐用 SVG Sprite(把所有 SVG 合并到一个文件中,通过 <use> 引用)。它既有雪碧图减少请求的优势,又保留了矢量图的清晰度和可维护性。

4.5 如何选择?一张决策表

说了这么多,你可能会问:到底该用哪种?我整理了一张决策表,直接对着选就行:

图片类型 推荐格式 压缩工具 备注
照片/实拍图 WebP(有损) cwebp / libwebp 兼容性不足时降级 JPEG
UI 切图/图标 PNG(8 位) PNGQuant 颜色少时效果极佳
矢量图标/插画 SVG SVGO 构建阶段自动执行
文字截图 PNG(无损) PNGQuant(高质量) 不要用有损格式
大量小图标(HTTP/1.1) 雪碧图 自动化拼图工具 HTTP/2 下可考虑 SVG Sprite
高压缩率需求 AVIF avifenc 仅用于支持 AVIF 的环境

最后说一句:图片压缩不是一次性的工作。我建议在 CI/CD 流程中加入图片压缩步骤,每次构建自动处理。这样开发同学只管上传原图,压缩和格式转换由流水线搞定——既保证质量,又不会有人忘记压缩。