此 Codelab 探讨了如何通过缩减应用的请求大小来缩减和压缩以下应用的 JavaScript 软件包,从而提高网页性能。
测量
在深入了解如何添加优化之前,最好先分析应用的当前状态。
- 如需预览网站,请按查看应用,然后按全屏 。
“移除未使用的代码”Codelab 中也介绍了此应用,可让您为喜爱的小猫投票。🐈
现在,我们来看看此应用的大小:
- 按 `Ctrl+Shift+J`(在 Mac 上,按 `Command+Option+J`)打开开发者工具。
- 点击网络标签页。
- 选中停用缓存复选框。
- 重新加载应用。
虽然在缩减此软件包大小方面的“移除未使用的代码”Codelab 中已经取得了很多进展,但 225 KB 仍然非常大。
缩减大小
请参考以下代码块。
function soNice() {
let counter = 0;
while (counter < 100) {
console.log('nice');
counter++;
}
}
如果此函数保存在它自己的文件中,则文件大小约为 112 B(字节)。
如果移除所有空格,生成的代码将如下所示:
function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}
现在,文件大小约为 83 B。如果因缩短变量名称的长度和修改某些表达式而导致其进一步损坏,最终代码可能如下所示:
function soNice(){for(let i=0;i<100;)console.log("nice"),i++}
文件大小现已达到 62 B。
每一步,代码的阅读难度都会增加。不过,浏览器的 JavaScript 引擎会以完全相同的方式解读上述每项信息。以这种方式混淆代码的优势有助于减小文件大小。一开始,112 B 确实很少,但大小仍然减少了 50%!
在该应用中,webpack 版本 4 用作模块打包器。具体版本可在 package.json
中查看。
"devDependencies": {
//...
"webpack": "^4.16.4",
//...
}
默认情况下,版本 4 在生产模式下已缩减 bundle 的大小。它使用 TerserWebpackPlugin
这一 Terser 插件。Terser 是一种用于压缩 JavaScript 代码的常用工具。
如需了解缩减后代码的外观,请在开发者工具 Network 面板中点击 main.bundle.js
。现在,点击响应标签页。
经过缩减和破坏的最终形式代码会显示在响应正文中。
如需了解软件包在不缩减大小的情况下的大小可能有多大,请打开 webpack.config.js
并更新 mode
配置。
module.exports = {
mode: 'production',
mode: 'none',
//...
重新加载应用,并通过开发者工具的 Network 面板再次查看软件包大小
差异很大!😅
请务必先还原此处的更改,然后再继续。
module.exports = {
mode: 'production',
mode: 'none',
//...
在应用中加入缩减代码大小的流程取决于您使用的工具:
- 如果使用 webpack v4 或更高版本,则无需执行任何额外操作,因为默认情况下,在生产模式下代码会缩减。👍
- 如果使用旧版 Webpack,请安装
TerserWebpackPlugin
并将其添加到 Webpack 构建流程中。本文档详细介绍了这一点。 - 您也可以使用其他缩减大小插件,例如 BabelMinifyWebpackPlugin 和 ClosureCompilerPlugin。
- 如果根本未使用模块打包器,请使用 Terser 作为 CLI 工具或直接将其添加为依赖项。
压缩
虽然“压缩”这一术语有时会松散地用于解释在缩减过程中如何缩减代码,但它实际上并不是从字面意义上的压缩。
压缩通常是指使用数据压缩算法修改过的代码。与最终提供完全有效的代码的缩减大小不同的是,压缩代码在使用前需要先进行解压缩。
对于每个 HTTP 请求和响应,浏览器和网络服务器都可以添加headers,以包含有关正在提取或接收的资产的更多信息。在开发者工具 Network 面板的 Headers
标签页中,您可以看到此情况,该标签页显示了三种类型:
- General 表示与整个请求-响应互动相关的常规标头。
- 响应标头显示了特定于服务器实际响应的标头列表。
- 请求标头显示了客户端附加到请求的标头列表。
查看 Request Headers
中的 accept-encoding
标头。
浏览器使用 accept-encoding
来指定支持哪些内容编码格式或压缩算法。市面上有很多文本压缩算法,但下面只有三种算法支持压缩(和解压缩)HTTP 网络请求:
- Gzip (
gzip
):用于服务器和客户端交互的最广泛压缩格式。它基于 Deflate 算法构建而成,目前所有浏览器都支持该算法。 - Deflate (
deflate
):不常用。 - Brotli (
br
):一种较新的压缩算法,旨在进一步提高压缩比,从而进一步加快页面加载速度。大多数浏览器的最新版本都支持此功能。
本教程中的示例应用与移除未使用的代码 Codelab 中完成的应用完全相同,区别在于 Express 现在用作服务器框架。在接下来的几个部分中,我们将介绍静态压缩和动态压缩。
动态压缩
动态压缩涉及在浏览器请求时对资源进行即时压缩。
优点
- 您无需创建和更新已保存的压缩版资源。
- 实时压缩效果特别适合动态生成的网页。
缺点
- 在更高的级别压缩文件以获得更好的压缩比需要更长的时间。如果用户在服务器发送素材资源之前需要等待压缩,这可能会导致性能下降。
使用 Node/Express 进行动态压缩
server.js
文件负责设置托管应用的节点服务器。
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/
目录中的所有静态 HTML、JS 和 CSS 文件(这些文件由 webpack 在每次构建时创建)。
为了确保所有资源在每次收到请求时都会进行压缩,可以使用压缩中间件库。首先,在 package.json
中将其添加为 devDependency
:
"devDependencies": {
//...
"compression": "^1.7.3"
},
将其导入服务器文件 server.js
:
const express = require('express');
const compression = require('compression');
在装载 express.static
之前,将其添加为中间件:
//...
const app = express();
app.use(compression());
app.use(express.static('public'));
//...
现在,重新加载应用,并在 Network 面板中查看 bundle 大小。
从 225 KB 到 61.6 KB!现在,在 Response Headers
中,content-encoding
标头表明服务器正在发送这个使用 gzip
编码的文件。
静态压缩
静态压缩背后的理念是提前压缩并节省资源。
优点
- 高压缩级别导致的延迟不再是问题。 无需实时进行压缩,因为现在可直接提取文件。
缺点
- 每次构建时都需要压缩资源。如果使用较高的压缩级别,构建时间可能会明显增加。
使用 Node/Express 和 webpack 进行静态压缩
由于静态压缩涉及预先压缩文件,因此您可以在构建步骤中修改 webpack 设置,以压缩资源。您可以使用 CompressionPlugin
实现此目的。
首先,在 package.json
中将其添加为 devDependency
:
"devDependencies": {
//...
"compression-webpack-plugin": "^1.1.11"
},
与任何其他 webpack 插件一样,将其导入配置文件 webpack.config.js:
中
const path = require("path");
//...
const CompressionPlugin = require("compression-webpack-plugin");
并将其包含在 plugins
数组中:
module.exports = {
//...
plugins: [
//...
new CompressionPlugin()
]
}
默认情况下,该插件会使用 gzip
压缩 build 文件。请查看此文档,了解如何添加选项以使用其他算法或包含/排除特定文件。
在应用重新加载并重新构建时,现在会创建主软件包的压缩版本。打开 Glitch 控制台,查看 Node 服务器处理的最终 public/
目录中的内容。
- 点击工具按钮。
- 点击控制台按钮。
- 在控制台中,运行以下命令以切换到
public
目录并查看其所有文件:
cd public
ls
软件包的 gzip 版本 main.bundle.js.gz
现在也保存在此处。默认情况下,CompressionPlugin
还会压缩 index.html
。
接下来,每当请求原始 JS 版本时,请告知服务器发送这些 gzip 压缩文件。为此,可以在使用 express.static
传送文件之前,在 server.js
中定义一个新路由。
const express = require('express'); const app = express(); app.get('*.js', (req, res, next) => { req.url = req.url + '.gz'; res.set('Content-Encoding', 'gzip'); next(); }); app.use(express.static('public')); //...
app.get
用于告知服务器如何响应对特定端点的 GET 请求。然后,系统将使用回调函数来定义如何处理此请求。路线的运作方式如下:
- 指定
'*.js'
作为第一个参数意味着这适用于每个为提取 JS 文件而触发的端点。 - 在回调中,
.gz
会附加到请求的网址,并且Content-Encoding
响应标头设置为gzip
。 - 最后,
next()
可确保相应序列继续执行下一个可能发生的任何回调。
应用重新加载后,请再次查看 Network
面板。
和以前一样,软件包大小显著减小!
总结
此 Codelab 介绍了缩减和压缩源代码的过程。 这两种技术成为当今许多工具的默认技术,因此请务必了解您的工具链是否已支持这两种技术,或者您是否应该开始自行应用这两个进程。