开启 Gzip、Brotli 压缩
约 1642 字大约 5 分钟
2025-06-29
前端预压缩
Webpack 配置 Gzip、Brotli 压缩的方案,属于前端预压缩。这种在构建阶段生成 .gz
、.br
文件的方式,与服务器实时压缩有本质区别。
预压缩的三大特征
1、构建阶段完成压缩
在构建时预先生成 .gz
、.br
文件,而非用户请求时压缩。
2、产物包含压缩文件
输出目录中同时存在压缩文件和原始文件,例如 main.js
、main.js.gz
、main.js.br
dist/
├─ main.js
├─ main.js.gz # 预压缩生成
├─ main.js.br # 预压缩生成
├─ style.css
├─ style.css.gz # 预压缩生成
├─ style.css.br # 预压缩生成
3、服务器直接使用
Nginx 等服务器只需配置相关参数,即可直接发送预压缩文件
# 优先匹配br
brotli off; # 禁用动态压缩
brotli_static on; # 优先使用预压缩文件(如果有)
# 优先匹配 gzip
gzip off; # 禁用动态压缩
gzip_static on; # 优先使用预压缩文件(如果有)
gunzip on; # 兼容不支持gzip的客户端
gzip_vary on; # 确保响应头包含 Vary: Accept-Encoding
与实时压缩的对比
传统的实时压缩方案,需要在服务器上配置 Gzip 模块,且每个请求都需要实时压缩。这样的操作比较考验服务器的配置和性能,而 Webpack 预压缩方案,在构建阶段一次性完成压缩,服务器无需额外配置,直接返回对应的 .gz
文件。
特性 | 预压缩 | 服务器实时压缩 |
---|---|---|
压缩时机 | 构建阶段 | 请求时实时压缩 |
CPU 消耗 | 构建时一次性消耗 | 每次请求消耗 CPU |
响应速度 | 极快(直接发送静态文件) | 较慢(需要实时压缩) |
适用场景 | 静态资源 | 动态 API 响应 |
部署要求 | 需上传.gz 文件 | 只需服务器启用 gzip 模块 |
开启 Gzip、Brotli 压缩
Webpack 配置示例
webpack.config.js
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
plugins: [
// Gzip 压缩
new CompressionPlugin({
filename: "[path][base].gz", // 输出文件名模式
algorithm: "gzip", // 使用gzip算法
test: /\.(js|css|html|json|svg|txt|eot|otf|ttf|woff|woff2)$/, // 压缩文件类型
threshold: 10240, // 只压缩大于10KB的文件
minRatio: 0.8, // 只有压缩率优于80%才会压缩
deleteOriginalAssets: false, // 是否删除原始文件
cache: true, // 启用缓存
compressionOptions: {
level: 9, // 最高压缩级别
},
}),
// Brotli 压缩(需 Node.js ≥ v11.7.0)
new CompressionPlugin({
filename: "[path][base].br",
algorithm: "brotliCompress", // 使用 Node.js 原生 Brotli
test: /\.(js|css|html|svg|json)$/,
threshold: 50 * 1024, // 只压缩 >50KB 的文件
minRatio: 0.8,
compressionOptions: {
level: 11, // Brotli 压缩级别 (1-11)
params: {
// 高级参数(可选)
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT, // 文本模式
[zlib.constants.BROTLI_PARAM_QUALITY]: 11, // 等同 level
},
},
}),
],
};
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
filename | String/Function | [path][base].gz | 生成的压缩文件名模式 |
algorithm | String/Function | gzip | 压缩算法,支持 gzip /deflate /deflateRaw |
test | RegExp/Array | 匹配所有文件 | 指定需要压缩的文件类型 |
threshold | Number | 0 | 文件大小阈值(字节),大于此值才压缩 |
minRatio | Number | 0.8 | 压缩比阈值,小于此值才压缩 |
deleteOriginalAssets | Boolean | false | 是否删除原始文件 |
cache | Boolean/String | false | 启用缓存或指定缓存目录 |
compressionOptions | Object | {} | 传递给 zlib 的选项 |
Nginx 服务器配置实例
Nginx 服务器需要根据具体情况配置,以下是一个简单的示例:
- 关闭动态压缩,优先使用压缩文件
- 优先匹配 brotli,gzip 作为备选
# 优先匹配br
brotli off; # 禁用动态压缩
brotli_static on; # 优先使用预压缩文件(如果有)
# 优先匹配 gzip
gzip off; # 禁用动态压缩
gzip_static on; # 优先使用预压缩文件(如果有)
gunzip on; # 兼容不支持gzip的客户端
gzip_vary on; # 确保响应头包含 Vary: Accept-Encoding
Node.js 作为服务器
例如使用 Express 作为服务器,手动实现需要注意以下几点:
- 需要根据请求头里的
Accept-Encoding
字段,判断浏览器支持的压缩格式。 - 如果浏览器不支持压缩,中间件不做任何处理,直接返回源文件。
- 如果浏览器支持压缩文件,则根据请求头返回对应的压缩文件,优先返回
.br
,其实选择.gz
。 - 需要根据对应压缩文件的内容,设置
Content-Encoding
、Content-Type
响应头。告知浏览器以什么样的格式去读取。
手动处理示例
const express = require("express");
const path = require("path");
const fs = require("fs");
const app = express();
const staticDir = path.join(__dirname, "dist");
// 中间件:检查并返回预压缩文件
app.use((req, res, next) => {
const acceptEncoding = req.headers["accept-encoding"] || "";
const filePath = path.join(staticDir, req.path);
// 检查原始文件是否存在
if (!fs.existsSync(filePath)) return next();
// 优先返回 Brotli
if (acceptEncoding.includes("br") && fs.existsSync(`${filePath}.br`)) {
req.url = `${req.url}.br`;
res.set("Content-Encoding", "br");
}
// 次优先返回 Gzip
else if (acceptEncoding.includes("gzip") && fs.existsSync(`${filePath}.gz`)) {
req.url = `${req.url}.gz`;
res.set("Content-Encoding", "gzip");
}
next();
});
// 静态文件服务
app.use(
express.static(staticDir, {
setHeaders: (res, filePath) => {
// 修正压缩文件的 Content-Type(移除 .gz/.br 后缀)
if (filePath.endsWith(".br") || filePath.endsWith(".gz")) {
const originalExt = path.extname(filePath.replace(/\.(br|gz)$/, ""));
res.set("Content-Type", getMimeType(originalExt));
}
},
})
);
// 获取 MIME 类型
function getMimeType(ext) {
const types = {
".js": "application/javascript",
".css": "text/css",
".html": "text/html",
".json": "application/json",
};
return types[ext] || "text/plain";
}
app.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});
中间件示例
或者使用中间件 express-static-gzip
实现。
const express = require("express");
const expressStaticGzip = require("express-static-gzip");
const app = express();
// 自动处理 .gz 和 .br 文件
app.use(
"/",
expressStaticGzip("dist", {
enableBrotli: true, // 启用 Brotli
orderPreference: ["br"], // 优先返回 Brotli
serveStatic: {
maxAge: "1y", // 缓存一年
cacheControl: true,
setHeaders: (res, path) => {
// 可自定义响应头
},
},
})
);
app.listen(3000);
验证 Gzip 是否生效
浏览器开发者工具检查
- 打开 Chrome 开发者工具(F12)
- 切换到 Network 标签
- 刷新页面
- 点击任意资源文件,查看响应头:
- 应有
content-encoding: gzip
- 比较
Content-Length
和资源实际大小
- 应有
性能优化建议
- 使用 webpack 开启预压缩,代替服务器实时压缩
- 优先使用 brotli,压缩等级直接拉满 11
- 使用 gzip 作为备选,压缩等级设置为 6
- 压缩阈值:
- 对于静态资源,如图片、字体等,压缩阈值设为 0,直接压缩。
- 对于动态内容,如 HTML、CSS、JS 等,根据实际情况调整,一般 10kb 、50kb 以上才压缩。
提示
一般 js、css、html 的压缩阈值设置为 50kb,这是因为 50kb 可以取得非常不错的压缩收益效果,可以非常显著的减少流量,而不会对性能产生太大影响。而一些小文件压缩完并没有太大的节省,对加载效果的改善非常有限,甚至是负优化。
更新日志
2025/8/24 08:17
查看所有更新日志
e7112
-1于