自从 Webpack 4 开始,生产环境构建默认使用 terseropen in new window 来压缩(minify)代码。本章来介绍下 Webpack 压缩代码的工作机制。

压缩(minify)JavaScript

这里说的压缩指的是缩小代码量,在不改变代码语义的情况下,重写代码。举个例子,比如将长的变量名重命名为短的变量名,将永远不可能到达的分支(if(false))删除。

在 Webpack 中,我们可以通过 optimization.minimizeoptimization.minimizer 这两个配置项来定制代码压缩过程。

Webpack 默认使用 terser-webpack-pluginopen in new window 这个插件来压缩代码,我们来尝试修改压缩逻辑。

npm add terser-webpack-plugin --develop
1

webpack.parts.js

const TerserPlugin = require('terser-webpack-plugin');

exports.minifyJavaScript = () => ({
  optimization: { minimizer: [new TerserPlugin()] },
});
1
2
3
4
5

webpack.config.js

const productionConfig = merge([

  parts.minifyJavaScript(),

  ...
]);
1
2
3
4
5
6

现在执行 npm run build 可以看到构建输出与之前相同。

可以通过 terserOptions 参数来自定义压缩过程,具体可以参考插件文档。

提高代码执行性能

作为代码压缩的补充,我们可以借助一些技术手段来提高代码执行性能。比如 scope hoisting。

从 Webpack 4 开始,在生产模式中默认启用 scope hoisting. 在构建过程中,Webpack 会将所有模块都放在一个作用域中,避免了为每一个模块都生成一个闭包。scope hoisting 会降低编译速度,但是换来的是代码执行性能的提升。

压缩 HTML

如果我们通过 html-loaderopen in new window 来处理 HTML 模板的话,可以使用 posthtmlopen in new windowposthtml-loaderopen in new window 来对 HTML 进行预处理,然后通过 posthtml-minifieropen in new window 来压缩 HTML,同时 posthtml-minify-classnamesopen in new window 可以缩小 HTML 中样式类名称的长度。

压缩 CSS

可以使用 css-minimizer-webpack-pluginopen in new window 这个插件来压缩 CSS。MiniCssExtractPlugin 插件只会简单的合并文本,当项目中有相同样式类的时候,会产生重复代码,css-minimizer-webpack-plugin 可以解决这个问题。css-minimizer-webpack-plugin 这个插件的底层基于 cssnanoopen in new window 实现。

首先安装依赖包

npm add css-minimizer-webpack-plugin --develop
1

webpack.parts.js

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

exports.minifyCSS = ({ options }) => ({
  optimization: {
    minimizer: [new CssMinimizerPlugin({ minimizerOptions: options })],
  },
});
1
2
3
4
5
6
7

webpack.config.js

const productionConfig = merge([
  parts.minifyJavaScript(),
  parts.minifyCSS({ options: { preset: ["default"] } }),
  ...
]);
1
2
3
4
5

此时执行 npm run build, 可以看到输出的 css 文件变小了。

⬡ webpack: Build Finished
⬡ webpack: assets by path *.js 129 KiB
    asset 935.js 126 KiB [emitted] [minimized] (id hint: vendors) 2 related assets
    asset main.js 3.28 KiB [emitted] [minimized] (name: main) 1 related asset
    asset 958.js 183 bytes [emitted] [minimized] 1 related asset
  asset main.css 1.37 KiB [emitted] [minimized] (name: main)
  asset index.html 259 bytes [emitted]
  Entrypoint main 131 KiB (182 KiB) = 935.js 126 KiB main.css 1.37 KiB main.js 3.28 KiB 2 auxiliary assets
  runtime modules 7.81 KiB 10 modules
  orphan modules 465 bytes [orphan] 2 modules
  code generated modules 133 KiB (javascript) 4.18 MiB (css/mini-extract) [code generated]
    modules by path ./node_modules/ 133 KiB
      modules by path ./node_modules/react/ 6.48 KiB 2 modules
      modules by path ./node_modules/react-dom/ 119 KiB 2 modules
      modules by path ./node_modules/scheduler/ 4.91 KiB
        ./node_modules/scheduler/index.js 198 bytes [built] [code generated]
        ./node_modules/scheduler/cjs/scheduler.production.min.js 4.72 KiB [built] [code generated]
      ./node_modules/object-assign/index.js 2.06 KiB [built] [code generated]
    modules by path ./src/ 633 bytes (javascript) 4.18 MiB (css/mini-extract)
      ./src/index.js + 2 modules 600 bytes [built] [code generated]
      css ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[0].use[2]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[0].use[3]!./src/main.css 4.18 MiB [code generated]
      ./src/lazy.js 33 bytes [built] [code generated]
  webpack 5.11.1 compiled successfully in 18304 ms
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

压缩输出的 Bundle

可以通过 gzip 或者 brotli 等压缩结束进一步压缩输出的文件大小。压缩后的代码会增加在浏览器端的解析时间,但是可以大大减少网络带宽占用。

在 Webpack 可以使用 compression-webpack-pluginopen in new window 这个插件来压缩代码。

混淆(Obfuscating)输出

obfuscator-loaderopen in new window 可以用来对代码进行混淆,是的代码在浏览器端不可读。

总结

代码压缩(minify) 可以用一种安全的转换方法来缩小代码量。

Webpack 在生产模式中,默认使用 Terser 来压缩代码。

除了压缩 JavaScript,我们还可以通过一些插件和 loader 来压缩 CSS 和 HTML。

关注微信公众号,获取最新推送~