高级前端
js
【Q137】js 代码压缩 minify 的原理是什么

title: "【Q137】js 代码压缩 minify 的原理是什么 | js,前端工程化高频面试题" description: "【Q137】js 代码压缩 minify 的原理是什么 字节跳动面试题、阿里腾讯面试题、美团小米面试题。"

js 代码压缩 minify 的原理是什么

更多描述 我们知道 javascript 代码经压缩 (uglify) 后,可以使体积变得更小,那它代码压缩的原理是什么。

如果你来做这么一个功能的话,你会怎么去压缩一段 js 代码的体积

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 138 (opens in a new tab)

Author 回答者: libin1991 (opens in a new tab)

@shfshanyue 问的是原理,你贴UglifyJS2的地址干嘛

Author 回答者: everlose (opens in a new tab)

uglify 包里有 ast.js 所以它一定是生成了抽象语法树 接着遍历语法树并作出优化,像是替换语法树中的变量,变成a,b,c那样的看不出意义的变量名。还有把 if/else 合并成三元运算符等。 最后输出代码的时候,全都输出成一行。

Author 回答者: fariellany (opens in a new tab)

uglify 包里有 ast.js 所以它一定是生成了抽象语法树 接着遍历语法树并作出优化,像是替换语法树中的变量,变成a,b,c那样的看不出意义的变量名。还有把 if/else 合并成三元运算符等。 最后输出代码的时候,全都输出成一行。

非常nice

Author 回答者: shfshanyue (opens in a new tab)

通过 AST 分析,根据选项配置一些策略,来生成一颗更小体积的 AST 并生成代码。

目前前端工程化中使用 terser (opens in a new tab)swc (opens in a new tab) 进行 JS 代码压缩,他们拥有相同的 API。

常见用以压缩 AST 的几种方案如下:

去除多余字符: 空格,换行及注释

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

此时文件大小是 62 Byte一般来说中文会占用更大的空间。

多余的空白字符会占用大量的体积,如空格,换行符,另外注释也会占用文件体积。当我们把所有的空白符合注释都去掉之后,代码体积会得到减少。

去掉多余字符之后,文件大小已经变为 30 Byte 压缩后代码如下:

function sum(a,b){return a+b}

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

有,比如多行代码压缩到一行时要注意行尾分号。

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

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

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

// 压缩: 缩短变量名
function sum (x, y) {
  return x + y;  
}
 
// 再压缩: 去除空余字符
function s(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

以及一个更复杂的例子,简直是杀手锏级别的优化。

// 压缩前
function hello () {
  console.log('hello, world')
}
 
hello()
 
// 压缩后
console.log('hello, world')