代码压缩是非常基础的优化手段。
虽然基础,但是性价比很高:经过压缩后的代码,体积被大幅度减小,这样就能大幅度节省网络传输资源,从而提升页面加载速度。
这里学习了webpack5压缩JS、压缩CSS、压缩HTML的方法:都是通过optimization的配置,配合一定的plugin来实现的(minimizer中指定,minimizer是个数组,可以放多个plugin)。
压缩JS
terser是目前最为流行的es6压缩工具。其前身是UglifyJS(它在 UglifyJS 基础上增加了 ES6 语法支持,并重构代码解析、压缩算法)。
下图为各大压缩工具的性能对比
webpack5.0 后默认使用 Terser 作为 JavaScript 代码压缩器,一般直接通过optimization.minimize 配置项,可开启(使用 mode = ‘production’ 启动生产模式构建时,默认也会开启 Terser 压缩。):
module.exports = {//...optimization: {minimize: true}};
只有 minimize = true’ 时才会调用 minimizer 声明的压缩器数组,执行压缩操作。
还有一些其他常见配置:
- dead_code:是否删除不可触达的代码 —— 也就是所谓的死代码;
 - booleans_as_integers:是否将 Boolean 值字面量转换为 0、1;
 - join_vars:是否合并连续的变量声明,如 var a = 1; var b = 2; 合并为 var a=1,b=2;
 
手动创建 terser-webpack-plugin 实例,也可以更精细的压缩代码:
const TerserPlugin = require("terser-webpack-plugin");module.exports = {// ...optimization: {minimize: true,minimizer: [new TerserPlugin({terserOptions: {compress: {reduce_vars: true,pure_funcs: ["console.log"],},// ...},}),],},};
这里列了一些常用的其他配置,更多配置见:
- test:只有命中该配置的产物路径才会执行压缩;
 - include:在该范围内的产物才会执行压缩;
 - exclude:与 include 相反,不在该范围内的产物才会执行压缩;
 - parallel:是否启动并行压缩,默认值为 true,此时会按 os.cpus().length - 1 启动若干进程并发执行(参考多进程并行:优化:并行构建);
 - minify:用于配置压缩器,支持传入自定义压缩函数,也支持 swc/esbuild/uglifyjs 等值;
 - terserOptions:传入 minify —— “压缩器”函数的配置参数;(terser-webpack-plugin并不只是 Terser 的简单包装,它更像是一个代码压缩功能骨架,底层还支持使用 SWC、UglifyJS、ESBuild 作为压缩器,使用时只需要通过 minify 参数切换即可, eg: https://webpack.js.org/plugins/terser-webpack-plugin/#swc)
 - extractComments:是否将代码中的备注抽取为单独文件,可配合特殊备注如 @license 使用。
压缩CSS
下面是压缩css配置逻辑: 
- 使用 mini-css-extract-plugin 将 CSS 代码抽取为单独的 CSS 产物文件,这样才能命中 css-minimizer-webpack-plugin 默认的 test 逻辑(loader这里用的是 
MiniCssExtractPlugin.loader而不是style-loader); - 使用 css-minimizer-webpack-plugin 压缩 CSS 代码。 ```javascript const CssMinimizerPlugin = require(“css-minimizer-webpack-plugin”); const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);
 
module.exports = {
  //…
  module: {
    rules: [
      {
        test: /.css$/,
        // 注意,这里用的是 MiniCssExtractPlugin.loader 而不是 style-loader
        use: [MiniCssExtractPlugin.loader, “css-loader”],
      },
    ],
  },
  optimization: {
    minimize: true,
    minimizer: [
      // Webpack5 之后,约定使用 '...' 字面量保留默认 minimizer 配置
      “…”,
      new CssMinimizerPlugin(),
    ],
  },
  // 需要使用 mini-css-extract-plugin 将 CSS 代码抽取为单独文件
  // 才能命中 css-minimizer-webpack-plugin 默认的 test 规则
  plugins: [new MiniCssExtractPlugin()],
};
与 terser-webpack-plugin 类似,css-minimizer-webpack-plugin 也支持 test、include、exclude、minify、minimizerOptions 配置,其中 minify 支持cssnano、clean-css、esbuild、paecel-css等:默认 cssnano 压缩代码,这个绝大多数场景就能满足需求;<a name="Aq68k"></a>#### 压缩HTML现代web框架中,html代码的比例不高,不过某些SSG(Static Site Generation)场景下可能就需要对html进行压缩了。webpack中可以使用这个:html-minifier-terser```javascriptconst HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");optimization: {minimize: true,minimizer: [// Webpack5 之后,约定使用 `'...'` 字面量保留默认 `minimizer` 配置"...",new HtmlMinimizerPlugin({minimizerOptions: {// 折叠 Boolean 型属性collapseBooleanAttributes: true,// 使用精简 `doctype` 定义useShortDoctype: true,// ...},}),],},
上面介绍了js、css、html的压缩,那么三种同时压缩最简单的配置:
optimization: {minimize: true, // terser压缩jsminimizer: [// Webpack5 之后,约定使用 `'...'` 字面量保留默认 `minimizer` 配置"...",// 压缩cssnew CssMinimizerPlugin(),// 压缩htmlnew HtmlMinimizerPlugin({minimizerOptions: {// 折叠 Boolean 型属性collapseBooleanAttributes: true,// 使用精简 `doctype` 定义useShortDoctype: true,// ...},}),],},
代码压缩原理
“代码压缩”最关键的问题是:如何用“更精简”的代码表达“同一套”程序逻辑?
- “更精简”意味着可以适当 —— 甚至完全牺牲可读性、语义、优雅度而力求用最少字符数的方式书写代码
 - “同一套”意味着修改前后必须保持一致的代码逻辑、执行流程、功能效果等;
 
那么,代码压缩的原理是什么呢?
- 转换为结构化、容易分析处理的 AST形态。
 - 在 AST 上做各种语法、语义、逻辑推理与简化替换;
 - 最后按精简过的 AST 生成结果代码。
 

社区曾经出现过非常非常多 JavaScript、HTML、CSS 代码压缩工具,基本上都是按照上面这种套路实现的,包括: Terser、ESBuild、CSS-Nano、babel-minify、htmlMinifierTerser 等,幸运的是,我们可以在 Webpack 中轻松接入这些工具,实现代码压缩
另外说说,代码压缩和代码混淆。
代码压缩的目的是为了减少代码体积,代码混淆的目的是降低代码的可读性使之变得不易理解;
两者的目的不同但是都使用了一些相同的手段,比如变量重命名,代码转换等。
代码混淆会有字符串提取提取加密,随机加入代码进行混淆等手段来降低代码可读性。这是与代码压缩有区别的地方。 
