Skip to main content

压缩

如何压缩

删除多余字符空格,换行和注释

// 对两个数求和
function sum(a, b) {
return a + b;
}

替换掉多余字符后会有什么问题产生呢?

有,比如多行代码压缩到一行时要注意行尾分号。 这就需要通过以下介绍的 AST 来解决。

压缩变量名,函数名及属性名

function sum(first, second) {
return first + second;
}

如以上 first 与 second 在函数的作用域中,在作用域外不会引用它,此时可以让它们的变量名称更短。但是如果这是一个 module 中,sum 这个函数也不会被导出呢?那可以把这个函数名也缩短。

// 压缩: 缩短变量名
function sum(x, y) {
return x + y;
}

在这个示例中,当完成代码压缩 (compress) 时,代码的混淆 (mangle) 也捎带完成。 但此时缩短变量的命名也需要 AST 支持,不至于在作用域中造成命名冲突。

解析程序逻辑:合并声明以及布尔值简化

// 压缩前
const a = 3;
const b = 4;

// 压缩后
const a = 3,
b = 4;

布尔值简化的示例如下:

// 压缩前
!b && !c && !d && !e;

// 压缩后
!(b || c || d || e);

解析程序逻辑: 编译预计算

// 压缩前
const ONE_YEAR = 365 * 24 * 60 * 60;

// 压缩后
const ONE_YAAR = 31536000;

AST

AST,抽象语法树,js 代码解析后的最小词法单元,而这个过程就是通过 Parser 来完成的。

那么 AST 可以做什么呢?

  • eslint: 校验你的代码风格
  • babel: 编译代码到 ES 低版本
  • taro/mpvue: 各种可以多端运行的小程序框架
  • GraphQL: 解析客户端查询

我们在日常工作中经常会不经意间与它打交道,如 eslint 与 babel,都会涉及到 js 与代码中游走。不同的解析器会生成不同的 AST,司空见惯的是 babel 使用的解析器 babylon,而 uglify 在代码压缩中使用到的解析器是 UglifyJS。

那压缩代码的过程:code -> AST -> (transform)一颗更小的 AST -> code,这与 babel 和 eslint 的流程一模一样。

uglify、terser 与 swc

一个久负盛名的关于代码压缩的库: uglify (opens new window),一个用以代码压缩混淆的库。但它有一个致命弱点,不支持 ES6。

一个更加适应现代化前端用以代码压缩的库 terser (opens new window)诞生了,它来自于 uglify,与它保持一致的 API,但是它对 ES6 有更好的支持,同时也是 webpack 内置进行代码压缩的库。

那它是如何完成一些压缩功能的,答案是 AST。

webpack 中内置的代码压缩插件就是使用了 terser

在 webpack 中压缩代码

在知道代码压缩是怎么完成的之后,我们终于可以把它搬到生产环境中去压缩代码。终于到了实践的时候了,虽然它只是简单的调用 API 并且调调参数。

一切与性能优化相关的都可以在 optimization 中找到,TerserPlugin 是一个底层基于 uglifyjs 的用来压缩 JS 的插件。