资源压缩入门:常见压缩算法概览

压缩算法这东西,说白了就是给资源「瘦身」。我在刚接触前端性能优化时,总觉得压缩是件很简单的事——找个工具跑一遍不就完了?后来踩过几次坑才发现,选错算法,有时候比不压缩还难受。

这一章,我带大家把几个主流压缩算法过一遍。不搞复杂的数学公式,咱们就聊它们怎么用、效果如何、坑在哪。

无损压缩 vs 有损压缩:先搞清楚这个

这两个概念是压缩的根基。我见过不少新手把 JS 文件用有损压缩跑了一遍,结果代码直接跑不起来……嗯,血的教训。

类型 特点 适用场景 典型算法
无损压缩 解压后数据与原始数据完全一致,不丢失任何信息 代码、文本、配置文件、数据库备份 Gzip、Brotli、Zstd、Deflate
有损压缩 丢弃部分人眼/人耳不敏感的信息,换取更高压缩比 图片、音频、视频 JPEG、WebP(有损模式)、MP3、AAC
注意:千万不要对 JavaScript、CSS、HTML 等文本资源使用有损压缩。我曾经见过有人用 JPEG 压缩算法去压 JSON 文件,结果数据全乱了。文本资源必须用无损压缩。

三大主流无损压缩算法

现在前端资源压缩,基本就是 Gzip、Brotli、Zstd 这三家争霸。我按自己的使用经验,一个一个说。

Gzip:老牌劲旅,兼容性之王

Gzip 诞生于 1992 年,到现在快 30 年了。它基于 Deflate 算法,压缩率中规中矩,但胜在所有浏览器和服务端都支持。

我在项目中遇到过一个场景:某客户的内网环境只允许 HTTP/1.1,而且服务器是古董级的 Nginx 1.10。这时候 Brotli 和 Zstd 都上不了,只能用 Gzip。说白了,Gzip 就是那个「不管环境多烂,它都能跑」的保底方案。

我的习惯:Gzip 压缩级别设为 6 就好。级别 9 虽然压缩率更高,但耗时是级别 6 的 2-3 倍,收益却只有 1-2%。生产环境没必要追求极致。

Brotli:Google 出品,现代 Web 的首选

Brotli 是 Google 在 2015 年推出的算法。它的压缩率比 Gzip 高 20-30%,而且针对 Web 资源(HTML、CSS、JS)做了专门优化。

我记得有一次优化一个 React 应用,Gzip 压缩后的 vendor.js 是 180KB,换成 Brotli 后直接降到 130KB。用户首屏加载时间从 2.8 秒降到了 2.1 秒。这个提升是实打实的。

不过 Brotli 有个坑:它需要 HTTPS 支持。因为 Brotli 压缩需要更高的 CPU 开销,浏览器只在 HTTPS 连接下才会接受 Brotli 压缩的资源。如果你的站点还是 HTTP,那 Brotli 就用不了。

一句话总结:能用 Brotli 就用 Brotli,它比 Gzip 小 20% 以上,而且现代浏览器(Chrome、Firefox、Edge、Safari 11+)都支持。

Zstd:后起之秀,速度与压缩比的平衡大师

Zstd(Zstandard)是 Facebook 在 2016 年开源的算法。它的特点是:压缩速度极快,解压速度更快。在同等压缩率下,Zstd 的压缩速度是 Gzip 的 3-5 倍,解压速度是 Gzip 的 10 倍以上。

我目前在两个场景下会优先用 Zstd:

  • 构建产物缓存:CI/CD 流水线中,把构建产物用 Zstd 压缩后上传到对象存储,速度比 Gzip 快很多。
  • 服务端资源预压缩:对于需要频繁读取的静态资源,用 Zstd 预压缩后存盘,读取时解压几乎不消耗 CPU。

但 Zstd 在浏览器端的支持还不够好。目前只有 Chrome 和 Firefox 的部分版本原生支持 Zstd 解压,所以不能直接用于 Web 资源传输。它更适合服务端内部的数据交换和存储。

压缩率与速度的权衡

你想想看,压缩算法本质上是在做一件事:用时间换空间。压缩级别越高,文件越小,但压缩耗时越长。这个权衡,在线上环境尤其重要。

我给大家看一组我实际测试的数据(测试文件:一个 2.3MB 的 Vue 应用打包后的 JS 文件):

算法 压缩级别 压缩后大小 压缩耗时 解压耗时
Gzip 6 680 KB 1.2 秒 0.3 秒
Gzip 9 650 KB 3.5 秒 0.3 秒
Brotli 4 520 KB 2.1 秒 0.2 秒
Brotli 11 480 KB 8.7 秒 0.2 秒
Zstd 3 560 KB 0.4 秒 0.05 秒
Zstd 19 460 KB 12.3 秒 0.05 秒

从这张表能看出几个规律:

  • Brotli 压缩率最高,但压缩耗时也最长。如果是在构建阶段预压缩,Brotli 级别 4-6 是比较好的平衡点。
  • Zstd 解压速度无敌,适合读多写少的场景。我习惯在 CI 缓存中用 Zstd 级别 3,又快又小。
  • Gzip 级别 6 是黄金档位,再往上提级别,收益递减明显。
避坑指南:我曾经在生产环境把 Brotli 级别设到 11,结果每次部署都要多等 10 秒。后来发现,用户根本感受不出 480KB 和 520KB 的差别,但部署慢 10 秒是实打实的痛苦。所以,不要盲目追求最高压缩级别,要根据实际场景选。

算法选择决策流程

说了这么多,到底该怎么选?我画了一张图,把决策逻辑串起来:

压缩算法选择决策流程 资源需要压缩 是否用于浏览器端传输? 是 否 是否支持 HTTPS? 使用 Brotli(推荐) 使用 Gzip(兼容) 服务端内部使用 追求极致速度? 是 使用 Zstd 否 使用 Brotli 或 Gzip 说明:Web 传输优先 Brotli(HTTPS 下),兼容场景用 Gzip;服务端内部优先 Zstd(速度优先)或 Brotli(压缩率优先)

实际项目中的配置示例

光说不练假把式。我给大家看几个实际配置,都是我在生产环境用过的。

Nginx 配置:同时支持 Gzip 和 Brotli

# 开启 Gzip
gzip on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

# 开启 Brotli(需要安装 ngx_brotli 模块)
brotli on;
brotli_comp_level 4;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

这里有个细节:Brotli 的级别 4 对应 Gzip 的级别 6,压缩率更高,速度还差不多。我建议你直接照搬这个配置。

Webpack 构建配置:预压缩静态资源

// webpack.config.js
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  plugins: [
    // 生成 .gz 文件
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,  // 只压缩大于 10KB 的文件
      minRatio: 0.8,     // 压缩率低于 0.8 才保留
    }),
    // 生成 .br 文件
    new CompressionPlugin({
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,
      minRatio: 0.8,
    }),
  ],
};

这样构建后,dist 目录下每个资源都有 .gz 和 .br 两个版本。Nginx 会根据请求头中的 Accept-Encoding 自动返回对应的压缩文件。

我的建议:预压缩比实时压缩好。构建阶段就把压缩做完,Nginx 只需要读取文件并返回,不消耗 CPU。我在大促期间见过实时压缩把 Nginx CPU 打满的惨案……从那以后,所有静态资源我都走预压缩。

本章小结

这一章我们聊了三个核心点:

  • 无损 vs 有损:代码和文本必须用无损,图片和音视频可以用有损。
  • 三大算法:Gzip 兼容性最好,Brotli 压缩率最高,Zstd 速度最快。
  • 权衡取舍:不要盲目追求最高压缩级别,要根据场景选合适的算法和级别。