前端项目的构建产物体积直接影响首屏加载速度。Webpack 打包出来的 bundle 文件,配合 HTTP 压缩传输,通常能减少 70% 以上的传输体积。虽然 Nginx 可以开启实时的 gzip 压缩,但对于流量较大的站点,实时压缩会持续消耗服务器 CPU。更优雅的做法是在构建阶段就把压缩好的文件生成出来,部署时直接使用静态压缩文件,服务器只做转发不做压缩。compression-webpack-plugin 就是干这件事的。

为什么要在构建阶段做压缩
先明确一个概念:浏览器和服务器之间协商的 gzip 或 brotli 压缩,针对的是传输过程,不是文件本身。浏览器在请求头里带上 Accept-Encoding: gzip, br,服务器看到这个头,就从磁盘上的原文件、gzip 文件或 brotli 文件中挑一个合适的返回,并在响应头里声明 Content-Encoding。浏览器收到后自动解压。
压缩这件事可以发生在两个时机。一是服务器收到请求时实时压缩,Nginx 的 gzip on 就是这种模式,优点是配置简单,缺点是每个请求都要现场压一遍,CPU 开销随流量线性增长。二是在构建阶段预压缩,把 app.js 同时生成 app.js.gz 和 app.js.br,Nginx 通过 gzip_static 和 brotli_static 指令直接返回现成的压缩文件,CPU 开销几乎为零。
预压缩还有个隐性好处:构建机的算力比线上服务器便宜得多,可以用更高的压缩等级换取更小的体积,而不用担心线上性能。比如 brotli 的最高压缩级别 11 在服务器上实时跑会非常慢,但在构建机上完全没问题。
compression-webpack-plugin 的安装与基础配置
插件通过 npm 安装,注意它要求 Node 版本和 Webpack 版本匹配,v10 版本需要 Webpack 5 及以上:
npm install compression-webpack-plugin --save-dev
最基础的配置只需要在 webpack 的 plugins 数组里加一段。下面的例子同时生成 gzip 和 brotli 两套文件:
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
// gzip 产物
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false
}),
// brotli 产物
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
compressionOptions: { level: 11 },
deleteOriginalAssets: false
})
]
};
几个关键参数解释一下。test 决定哪些文件参与压缩,图片类资源本身已经是压缩格式,再压一遍收益极低,一般只处理 js、css、html 和 svg。threshold 设置门槛,小于 10240 字节(10KB)的文件不压缩,因为小文件压缩收益可能抵不过解压开销。minRatio 表示压缩后体积与原体积的比值小于 0.8 才保留压缩文件,避免生成体积不降反升的产物。deleteOriginalAssets 要特别注意,设为 true 会删掉原始文件,如果没有配套的服务器静态压缩配置,页面会直接白屏,建议保持 false。
如果你用的是 Webpack 5,还有更简洁的写法,直接传 algorithm: 'brotliCompress' 即可,插件内部会调用 Node 的 zlib 模块,无需额外依赖。filename 占位符中的 [path][base] 会还原成原文件的相对路径和文件名,保证输出目录结构与原文件一一对应。
gzip 与 brotli 怎么选,能一起用吗
答案是能,而且推荐一起用。brotli 是 Google 推出的压缩算法,针对 Web 场景做了大量优化,内置了一份针对 HTML、CSS、JS 的字典,同等压缩级别下体积普遍比 gzip 小 15% 到 25%。但它的浏览器支持没有 gzip 那么彻底,虽然现代浏览器基本都支持,一些老旧客户端或特殊环境可能只认 gzip。
两套文件同时部署后,由服务器根据请求头来决定返回哪个。浏览器的 Accept-Encoding 头里如果同时包含 br 和 gzip,支持 brotli 的 Nginx 模块会优先返回 .br 文件。这里有个前提:Nginx 的 brotli 支持不是自带的,需要编译 ngx_brotli 模块,很多云厂商的镜像或 OpenResty 发行版已经内置。
# Nginx 静态压缩配置
http {
gzip_static on;
brotli_static on;
# 实时压缩作为兜底,处理没有预压缩文件的资源
gzip on;
gzip_comp_level 5;
gzip_types text/plain text/css application/javascript application/json;
}
注意 gzip_static on 只会对带 .gz 后缀的文件做匹配,找不到静态文件时会回退到原文件,再由实时压缩兜底。这套三层结构(brotli 静态、gzip 静态、gzip 实时)能覆盖几乎所有客户端场景。
多环境配置与常见踩坑点
开发环境完全没必要开压缩,不仅拖慢构建速度,dev server 的内存读取也不需要压缩产物。建议只在生产配置中启用,比如 vue.config.js 或 webpack.prod.js 中按环境变量判断。另外一个常见坑是把 deleteOriginalAssets 设为 true 之后又没配静态压缩,请求返回的是 .gz 文件但响应头没有正确的 Content-Type,浏览器把二进制当 JS 解析直接报语法错误。排查方法是看网络面板里响应的 Content-Encoding 头。
还有 CDN 场景需要注意:如果静态资源走 CDN,要确认 CDN 开启了回源时的压缩协商,否则辛辛苦苦生成的 .br 文件可能根本不会被下发。可以在浏览器控制台用 curl 模拟验证:
curl -I -H "Accept-Encoding: gzip" https://your-domain.com/static/js/app.js curl -I -H "Accept-Encoding: br" https://your-domain.com/static/js/app.js
两条命令的响应头如果分别出现 Content-Encoding: gzip 和 Content-Encoding: br,说明整套链路已经跑通。最后提一下构建耗时,brotli level 11 对大项目来说可能多花一两分钟,如果 CI 流水线时间敏感,可以降到 9 或 10,体积差异通常在 1% 以内,压缩速度却明显更快。合理利用这些参数,配合静态资源缓存策略,首屏加载体验会有肉眼可见的提升。
Webpackcompression-webpack-pluginbrotli压缩修改时间:2026-09-16 03:56:32