一、什么是 Terser?
Terser 像是代码的“健身教练”,专门帮 JS 减肥。它从 UglifyJS 演化而来(UglifyJS 的现代版),支持 ES6+ 语法,能在压缩的同时保持代码功能不变。Webpack 用它来处理生产环境的 JS 文件,去掉注释、缩短变量名、优化表达式,最终输出一个精简版
二、Terser 的核心原理
Terser 的工作可以分成几个步骤,简单来说就是“分析 → 转换 → 输出”:
1、解析代码(Parse)
把 JS 代码转成 抽象语法树(AST) ,就像把文章拆成词和句子
比如:
// 这是一个函数
function add(a, b) {
return a + b;
}
转换成的 AST 大概是以下
{
"type": "Program",
"body": [
{
"type": "FunctionDeclaration",
"id": {
"type": "Identifier",
"name": "add"
},
"params": [
{
"type": "Identifier",
"name": "a"
},
{
"type": "Identifier",
"name": "b"
}
],
"body": {
"type": "BlockStatement",
"body": [
{
"type": "ReturnStatement",
"argument": {
"type": "BinaryExpression",
"operator": "+",
"left": {
"type": "Identifier",
"name": "a"
},
"right": {
"type": "Identifier",
"name": "b"
}
}
}
]
}
}
],
"sourceType": "script"
}
图解:
2、压缩与优化
-
去掉无用东西:注释、空格、换行全删
上面的代码变成:
function add(a,b){return a+b} -
缩短名字:把变量名、函数名改短,不影响逻辑
可能变成:function a(b,c){return b+c}
-
优化逻辑:合并表达式、删死代码(dead code)
if (true) { console.log('hi'); } else { console.log('no'); }
// 优化成
console.log('hi')
3、生成代码
把 AST 转回 JS 字符串,输出最终文件
加个 Source Map(可选),方便调试时映射回原始代码
Terser 从 AST 生成 JS 字符串时,加入 Source Map 是个“边走边记”的过程
Terser 用一个叫 SourceMapGenerator 的工具(基于 Mozilla 的 source-map 库),在生成压缩代码的同时,记录每个字符的原始位置
// 原始代码 function add(a, b) { return a + b; } // 压缩后: function a(b,c){return b+c}{ "version": 3, "sources": ["input.js"], "names": ["add", "a", "b"], // mappings 核心字段,用 VLQ 编码表示压缩代码和原始代码的对应位置 "mappings": "AAAA,QAASA,IAAMC,EAAEC,GACf,OAAOA,EAAEC" }
Source Map 原理细节
Source Map 的本质:它是个查找表,记录了压缩代码的每个字符(或 token)在原始代码里的位置。
VLQ 编码:为了节省空间,映射信息用 base64 VLQ 压缩,比如 AAAA 表示“第 1 行第 1 列偏移 0”。
Terser 的实现:
用 source-map 库的 SourceMapGenerator 类。
每次生成一个 token(比如变量名 a),就调用 addMapping 方法:
generator.addMapping({
generated: { line: 1, column: 9 }, // 压缩代码位置
original: { line: 1, column: 8 }, // 原始代码位置
source: 'input.js',
name: 'add'
});