此代码实验室探讨了缩小和压缩以下应用程序的 JavaScript 捆绑包如何通过减小应用程序的请求大小来提高页面性能。
测量
在深入添加优化之前,最好先分析应用程序的当前状态。
- 要预览网站,请按查看应用。然后按全屏
。
此应用程序在 “删除未使用的代码” 代码实验室中也有介绍,它允许您为您最喜欢的小猫投票。🐈
现在看看这个应用程序有多大
- 按 `Control+Shift+J`(在 Mac 上按 `Command+Option+J`)打开 DevTools。
- 点击 Network 选项卡。
- 选中 Disable cache 复选框。
- 重新加载应用程序。
尽管在 “删除未使用的代码” 代码实验室中在减小此捆绑包大小方面取得了很大进展,但 225 KB 仍然很大。
缩小
考虑以下代码块。
function soNice() {
let counter = 0;
while (counter < 100) {
console.log('nice');
counter++;
}
}
如果此函数保存在其自己的文件中,则文件大小约为 112 字节(bytes)。
如果删除所有空格,则生成的代码如下所示
function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}
现在文件大小约为 83 字节。如果通过缩短变量名长度和修改某些表达式进一步进行处理,则最终代码可能如下所示
function soNice(){for(let i=0;i<100;)console.log("nice"),i++}
现在文件大小达到 62 字节。
每一步,代码都变得越来越难以阅读。但是,浏览器的 JavaScript 引擎以完全相同的方式解释这些代码。以这种方式混淆代码的好处是可以帮助实现更小的文件大小。112 字节一开始确实不多,但大小仍然减少了 50%!
在此应用程序中,webpack 版本 4 用作模块捆绑器。可以在 package.json
中查看特定版本。
"devDependencies": {
//...
"webpack": "^4.16.4",
//...
}
版本 4 在生产模式下默认已缩小捆绑包。它使用 TerserWebpackPlugin
,这是 Terser 的一个插件。Terser 是一种流行的工具,用于压缩 JavaScript 代码。
要了解缩小后的代码是什么样子,请继续点击 DevTools Network 面板中的 main.bundle.js
。现在点击 Response 选项卡。
最终形式的代码,经过缩小和混淆,显示在响应正文中。要了解如果未缩小捆绑包,它可能有多大,请打开 webpack.config.js
并更新 mode
配置。
module.exports = {
mode: 'production',
mode: 'none',
//...
重新加载应用程序,并通过 DevTools Network 面板再次查看捆绑包大小
这是一个非常大的差异!😅
在继续之前,请务必恢复此处的更改。
module.exports = {
mode: 'production',
mode: 'none',
//...
在您的应用程序中包含缩小代码的过程取决于您使用的工具
- 如果使用 webpack v4 或更高版本,则无需进行额外的工作,因为代码在生产模式下默认已缩小。👍
- 如果使用旧版本的 webpack,请安装并将
TerserWebpackPlugin
包含到 webpack 构建过程中。文档详细解释了这一点。 - 还存在其他缩小插件,可以替代使用,例如 BabelMinifyWebpackPlugin 和 ClosureCompilerPlugin。
- 如果根本没有使用模块捆绑器,请使用 Terser 作为 CLI 工具,或直接将其作为依赖项包含在内。
压缩
尽管术语“压缩”有时被松散地用来解释代码在缩小过程中是如何减少的,但它实际上并不是字面意义上的压缩。
压缩通常指使用数据压缩算法修改过的代码。与最终提供完全有效代码的缩小不同,压缩代码在使用前需要解压缩。
对于每个 HTTP 请求和响应,浏览器和 Web 服务器都可以添加 标头,以包含有关正在获取或接收的资产的附加信息。这可以在 DevTools Network 面板中的 Headers
选项卡中看到,其中显示了三种类型
- General 表示与整个请求-响应交互相关的通用标头。
- Response Headers 显示服务器实际响应的特定标头列表。
- Request Headers 显示客户端附加到请求的标头列表。
查看 Request Headers
中的 accept-encoding
标头。
accept-encoding
供浏览器用于指定它支持的内容编码格式或压缩算法。有很多文本压缩算法,但此处仅支持三种用于 HTTP 网络请求的压缩(和解压缩)
- Gzip (
gzip
):用于服务器和客户端交互的最广泛使用的压缩格式。它建立在 Deflate 算法之上,并受所有当前浏览器的支持。 - Deflate (
deflate
):不常用。 - Brotli (
br
):一种较新的压缩算法,旨在进一步提高压缩率,从而可以实现更快的页面加载速度。它在大多数浏览器的最新版本中受支持。
本教程中的示例应用程序与 “删除未使用的代码” 代码实验室中完成的应用程序相同,不同之处在于现在 Express 用作服务器框架。在接下来的几个部分中,将探讨静态和动态压缩。
动态压缩
动态压缩涉及在浏览器请求资产时即时压缩资产。
优点
- 无需创建和更新已保存的资产压缩版本。
- 即时压缩对于动态生成的网页尤其有效。
缺点
- 以更高的级别压缩文件以实现更好的压缩率需要更长的时间。这可能会导致性能下降,因为用户需要等待资产压缩后才能由服务器发送。
使用 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/
目录中的所有静态 HTML、JS 和 CSS 文件(这些文件由 webpack 在每次构建时创建)。
为了确保每次请求资产时都对其进行压缩,可以使用 compression 中间件库。首先将其作为 devDependency
添加到 package.json
中
"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 面板中查看捆绑包大小。
从 225 KB 降至 61.6 KB!现在在 Response Headers
中,content-encoding
标头显示服务器正在发送使用 gzip
编码的此文件。
静态压缩
静态压缩背后的想法是预先压缩和保存资产。
优点
- 由于高压缩级别而导致的延迟不再是问题。无需即时发生任何事情来压缩文件,因为现在可以直接获取它们。
缺点
- 每次构建都需要压缩资产。如果使用高压缩级别,构建时间可能会显着增加。
使用 Node/Express 和 webpack 进行静态压缩
由于静态压缩涉及预先压缩文件,因此可以修改 webpack 设置以在构建步骤中压缩资产。 CompressionPlugin
可以用于此目的。
首先将其作为 devDependency
添加到 package.json
中
"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
压缩构建文件。查看 文档,了解如何添加选项以使用不同的算法或包含/排除某些文件。
当应用程序重新加载并重建时,现在会创建主捆绑包的压缩版本。打开 Glitch 控制台以查看 Node 服务器提供的最终 public/
目录中的内容。
- 点击 Tools 按钮。
- 点击 Console 按钮。
- 在控制台中,运行以下命令以更改为
public
目录并查看其所有文件
cd public
ls
捆绑包的 gzipped 版本 main.bundle.js.gz
现在也保存在此处。 CompressionPlugin
默认情况下还会压缩 index.html
。
接下来需要做的是告诉服务器,每当请求其原始 JS 版本时,都发送这些 gzipped 文件。这可以通过在 server.js
中的 express.static
提供文件之前定义新路由来完成。
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
附加到请求的 URL,并且Content-Encoding
响应标头设置为gzip
。 - 最后,
next()
确保序列继续到下一个可能的任何回调。
应用程序重新加载后,再次查看 Network
面板。
与之前一样,捆绑包大小显着减小!
结论
本代码实验室介绍了缩小和压缩源代码的过程。这两种技术都正在成为当今许多可用工具中的默认设置,因此务必了解您的工具链是否已支持它们,或者您是否应该开始自己应用这两个过程。