使用 Brotli 缩小和压缩网络有效负载

本代码实验室是缩小和压缩网络有效负载代码实验室的扩展,并假定您熟悉压缩的基本概念。与其他压缩算法(如 gzip)相比,本代码实验室探讨了 Brotli 压缩 (br) 如何进一步降低压缩率和应用的总大小。

App screenshot

测量

在深入添加优化之前,最好先分析应用的当前状态。

  1. 点击 Remix to Edit(混合以编辑) 使项目可编辑。
  2. 要预览网站,请按 View App(查看应用)。然后按 Fullscreen(全屏) fullscreen

在上一个缩小和压缩网络有效负载代码实验室中,我们将 main.js 的大小从 225 KB 减小到 61.6 KB。在本代码实验室中,您将探索 Brotli 压缩如何进一步减小此捆绑包大小。

Brotli 压缩

Brotli 是一种较新的压缩算法,可以提供比 gzip 更好的文本压缩结果。根据 CertSimple,Brotli 的性能是:

  • 对于 JavaScript,比 gzip 小 14%
  • 对于 HTML,比 gzip 小 21%
  • 对于 CSS,比 gzip 小 17%

要使用 Brotli,您的服务器必须支持 HTTPS。 所有现代浏览器都支持 Brotli。支持 Brotli 的浏览器将在 Accept-Encoding 标头中包含 br

Accept-Encoding: gzip, deflate, br

您可以使用 Chrome 开发者工具 Network 选项卡中的 Content-Encoding 字段 (Command+Option+ICtrl+Alt+I) 确定使用的压缩算法

Network panel. The Content-encoding column displays encodings used for various assets, encluding gzip and brotli (br).

如何启用 Brotli

如何设置 Web 服务器以发送 Brotli 编码的资源取决于您计划如何对它们进行编码。您的选项是在请求时使用 Brotli 动态压缩资源(动态),或提前对它们进行编码,以便在用户请求它们时它们已经被压缩(静态)。

动态压缩

动态压缩 涉及在浏览器请求资产时即时压缩它们。

优点

  • 无需创建和更新资产的已保存压缩版本。
  • 即时压缩对于动态生成的网页尤其有效。

缺点

  • 以更高的级别压缩文件以实现更好的压缩率需要更长的时间。这可能会导致性能下降,因为用户在服务器发送资产之前等待资产压缩。

使用 Node 和 Express 进行动态压缩

server.js 文件负责设置托管应用的 Node 服务器。

const express = require('express');
const app = express();
app.use(express.static('public'));

const 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 模块。首先将其作为 devDependency 添加到 package.json

"devDependencies": {
  // ...
  "shrink-ray": "^0.1.3"
},

并将其导入到服务器文件 server.js

const express = require('express');
const shrinkRay = require('shrink-ray');

并在安装 express.static 之前将其添加为中间件

// ...
const app = express();

// Compress all requests
app.use(shrinkRay());
app.use(express.static('public'));

现在重新加载应用,并在 Network 面板中查看捆绑包大小

Bundle size with dynamic Brotli compression.

您现在可以在 Content-Encoding 标头中看到 brotlibz 应用。main.bundle.js225 KB 减小到 53.1 KB!与 gzip (61.6 KB) 相比,这小了约 14%。

静态压缩

静态压缩背后的想法是提前压缩和保存资产。

优点

  • 高压缩级别造成的延迟不再是问题。现在可以直接获取文件,无需即时压缩文件。

缺点

  • 每次构建都需要压缩资产。如果使用高压缩级别,构建时间可能会显着增加。

使用 Node 和 Express 以及 webpack 进行静态压缩

由于 静态压缩 涉及提前压缩文件,因此可以修改 webpack 设置以在构建步骤中压缩资产。 brotli-webpack-plugin 可以用于此目的。

首先将其作为 devDependency 添加到 package.json

"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:与此 RegExp 匹配的所有资产(即,以 .js 结尾的 JavaScript 资产)都将被处理。

例如,main.js 将被重命名为 main.js.br

当应用重新加载和重建时,现在将创建主捆绑包的压缩版本。打开 Glitch Console 以查看最终 public/ 目录(由 Node 服务器提供)中的内容。

  1. 点击 Tools(工具) 按钮。
  2. 点击 Console(控制台) 按钮。
  3. 在控制台中,运行以下命令以更改为 public 目录并查看其所有文件
cd public
ls -lh
Bundle size with static Brotli compression

捆绑包的 brotli 压缩版本 main.bundle.js.br 现在也保存在此处,并且比 main.bundle.js 小约 76%(225 KB 对比 53 KB)。

接下来,告知服务器在请求其原始 JS 版本时发送这些 brotli 压缩的文件。这可以通过在 server.js 中定义一个新路由来完成,在文件通过 express.static 提供之前。

const express = require('express');
const 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 附加到请求的 URL,并将 Content-Encoding 响应标头设置为 br
  • Content-Type 标头设置为 application/javascript; charset=UTF-8 以指定 MIME 类型。
  • 最后,next() 确保序列继续到可能存在的任何下一个回调。

由于某些浏览器可能不支持 brotli 压缩,请通过检查 Accept-Encoding 请求标头是否包含 br 来确认 brotli 是否受支持,然后再返回 brotli 压缩的文件

const express = require('express');
const 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'));

应用重新加载后,再次查看 Network 面板。

Bundle size of 53.1 KB (from 225KB)

成功!您已使用 Brotli 压缩来进一步压缩您的资产!

结论

本代码实验室说明了 brotli 如何进一步减小应用的总大小。在支持的情况下,brotli 是一种比 gzip 更强大的压缩算法。