此 Codelab 是“缩减和压缩网络载荷”Codelab 的扩展内容,并假定您熟悉压缩的基本概念。与 gzip
等其他压缩算法相比,此 Codelab 探索了 Brotli 压缩如何进一步降低压缩比率和缩减应用的总体大小。
测量
在深入了解添加优化措施之前,最好先分析应用的当前状态。
- 点击 Remix to Edit 使项目可修改。
- 如需预览网站,请按查看应用,然后按全屏 。
在之前的“缩减和压缩网络载荷”Codelab 中,我们将 main.js
的大小从 225 KB 缩减到了 61.6 KB。在此 Codelab 中,您将探索 Brotli 压缩如何进一步缩减此 bundle 大小。
Brotli 压缩
Brotli 是一种较新的压缩算法,可以提供比 gzip
更好的文本压缩结果。根据 CertSimple,Brotli 的性能为:
- 比用于 JavaScript 的
gzip
小 14% - 比
gzip
小 21%(对于 HTML) - 比
gzip
小 17%(适用于 CSS)
要使用 Brotli,您的服务器必须支持 HTTPS。大多数浏览器的最新版本都支持 Brotli。支持 Brotli 的浏览器会在 Accept-Encoding
头文件中包含 br
:
Accept-Encoding: gzip, deflate, br
您可以通过 Chrome 开发者工具“网络”标签页(Command+Option+I
或 Ctrl+Alt+I
)中的 Content-Encoding
字段确定使用哪种压缩算法:
启用 Brotli
动态压缩
动态压缩涉及到在浏览器请求时即时压缩资源。
优点
- 不需要创建和更新资源的已保存压缩版本。
- 即时压缩功能尤其适用于动态生成的网页。
缺点
- 为了获得更好的压缩比,压缩较高级别的文件需要花费更长时间。当用户等待资源在服务器发送之前进行压缩时,这可能会导致性能下降。
使用 Node/Express 动态压缩
server.js
文件负责设置托管应用的节点服务器。
var express = require('express');
var app = express();
app.use(express.static('public'));
var listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});
目前所做的只是导入 express
,并使用 express.static
中间件加载 public/directory
中的所有静态 HTML、JS 和 CSS 文件(这些文件由 webpack 在每次构建时创建)。
为确保每次请求时都使用 brotli 压缩所有资源,可以使用 shrink-ray
模块。首先,在 package.json
中将其作为 devDependency
添加:
"devDependencies": {
//...
"shrink-ray": "^0.1.3"
},
然后将其导入到服务器文件 server.js
中:
var express = require('express');
var shrinkRay = require('shrink-ray');
在装载 express.static
之前,请将其作为中间件添加:
//...
var app = express();
// compress all requests
app.use(shrinkRay());
app.use(express.static('public'));
现在,重新加载应用,并在“Network”面板中查看 bundle 大小:
您现在可以在 Content-Encoding
标头中看到从 bz
应用 brotli
的情况。main.bundle.js
已从 225 KB 缩减到 53.1 KB!与 gzip
(61.6 KB) 相比,该大小约降低了 14%。
静态压缩
静态压缩背后的理念是提前压缩并保存资源。
优点
- 因此,您不必再担心因压缩级别过高而导致的延迟。无需任何动态文件即可压缩文件,因为文件现在可以直接提取。
缺点
- 每次构建时都需要压缩资源。如果使用高压缩级别,构建时间可能会显著增加。
使用 Node/Express 和 webpack 进行静态压缩
由于静态压缩涉及提前压缩文件,因此可以在构建步骤中修改 webpack 设置以压缩资源。为此,可以使用 brotli-webpack-plugin
。
首先,在 package.json
中将其作为 devDependency
添加:
"devDependencies": {
//...
"brotli-webpack-plugin": "^1.1.0"
},
与任何其他 Webpack 插件一样,请将其导入到配置文件 webpack.config.js
中:
var path = require("path");
//...
var BrotliPlugin = require('brotli-webpack-plugin');
并将其包含在 plugins 数组中:
module.exports = {
// ...
plugins: [
// ...
new BrotliPlugin({
asset: '[file].br',
test: /\.(js)$/
})
]
},
该插件数组使用以下参数:
asset
:目标素材资源的名称。[file]
已替换为原始素材资源文件名。test
:系统会处理与此正则表达式匹配的所有资源(即以.js
结尾的 JavaScript 资源)。
例如,main.js
将重命名为 main.js.br
。
当应用重新加载并重新构建时,现在会创建主软件包的压缩版本。打开 Glitch 控制台,查看节点服务器提供的最终 public/
目录中的内容。
- 点击工具按钮。
- 点击控制台按钮。
- 在控制台中,运行以下命令切换到
public
目录并查看其所有文件:
cd public
ls -lh
软件包的 brotli 压缩版本 main.bundle.js.br
现在也保存在此处,其大小比 main.bundle.js
小约 76%(225 KB 比 53 KB)。
接下来,指示服务器在每次请求这些 brotli 压缩文件的原始 JS 版本时发送它们。这可以通过在 server.js
中定义一个新路由来实现,然后再使用 express.static
传送文件。
var express = require('express');
var app = express();
app.get('*.js', (req, res, next) => {
req.url = req.url + '.br';
res.set('Content-Encoding', 'br');
res.set('Content-Type', 'application/javascript; charset=UTF-8');
next();
});
app.use(express.static('public'));
app.get
用于告知服务器如何响应针对特定端点的 GET
请求。然后,会使用回调函数定义如何处理此请求。该路线的工作原理如下:
- 指定
'*.js'
作为第一个参数意味着这适用于为提取 JS 文件而触发的每个端点。 - 在回调中,
.br
会附加到请求的网址,并且Content-Encoding
响应标头会设置为br
。 Content-Type
标头设置为application/javascript; charset=UTF-8
以指定 MIME 类型。- 最后,
next()
可确保序列继续执行下一个可能进行的回调。
由于某些浏览器可能不支持 brotli 压缩,因此请在返回 brotli 压缩的文件之前确认是否支持 brotli,具体方法是检查 Accept-Encoding
请求标头是否包含 br
:
var express = require('express');
var app = express();
app.get('*.js', (req, res, next) => {
if (req.header('Accept-Encoding').includes('br')) {
req.url = req.url + '.br';
console.log(req.header('Accept-Encoding'));
res.set('Content-Encoding', 'br');
res.set('Content-Type', 'application/javascript; charset=UTF-8');
}
next();
});
app.use(express.static('public'));
应用重新加载后,请再次查看“网络”面板。
成功!您已使用 Brotli 压缩功能进一步压缩了资源!
总结
此 Codelab 说明了 brotli
如何进一步缩减应用的总大小。在支持的情况下,brotli
是比 gzip
更强大的压缩算法。