Babel 与 AST
解释 Babel 如何利用 AST 进行 JavaScript 代码的语法转换和分析。
#tech / dev / frontend
#type / synthesis
#status / growing
#resource / javascript
[!info] related notes
- 所属 MOC: 构建工具 MOC, 前端工程化 MOC
- 前置概念: webpack 构建流程
- 并列概念: webpack loader 和 plugin
Babel 与 AST
这篇笔记回答两个问题:AST 是什么,以及 Babel 如何利用 AST 完成代码转换。
一句话定义
AST(抽象语法树)是代码的结构化树形表示,Babel 通过”解析 -> 转换 -> 生成”三步利用 AST 完成 JS 语法转换。
AST 是什么
AST 是源代码语法结构的树状抽象。每个节点代表代码中的一个语法构造。
const a = 1 + 2;
对应的 AST 大致为:
VariableDeclaration
└── VariableDeclarator
├── id: Identifier("a")
└── init: BinaryExpression
├── operator: "+"
├── left: NumericLiteral(1)
└── right: NumericLiteral(2)
Babel 三阶段
1. Parse(解析)
将源代码字符串解析为 AST。使用 @babel/parser(基于 acorn)。
源代码 -> Token 流 -> AST
2. Transform(转换)
遍历 AST,通过 visitor 模式对节点进行增删改。每个 plugin 就是一个 visitor。
// 示例:将箭头函数转为普通函数
const arrowPlugin = {
visitor: {
ArrowFunctionExpression(path) {
path.replaceWith(
t.functionExpression(null, path.node.params, path.node.body)
);
}
}
};
3. Generate(生成)
将转换后的 AST 重新生成为目标代码字符串,同时生成 Source Map。
AST -> 目标代码 + Source Map
常见 Babel Plugin / Preset
@babel/preset-env: 根据目标环境自动选择语法转换@babel/plugin-transform-arrow-functions: 箭头函数转换@babel/plugin-transform-classes: class 语法转换@babel/plugin-transform-runtime: 复用 helper 函数,减少产物体积
AST 的其他用途
- ESLint:遍历 AST 检查代码规范
- Prettier:基于 AST 重新格式化代码
- webpack tree-shaking:基于 AST 分析静态导入导出
- 代码压缩:基于 AST 删除无用代码
常见边界
- AST 不包含注释、空格、换行等格式信息(除非特别配置)
- 不同 parser 生成的 AST 格式可能不同(ESTree、Babel AST、TypeScript AST)
- Babel 只做语法转换,不做类型检查(TypeScript 需要额外处理)
最小例子
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const code = 'const add = (a, b) => a + b;';
// 1. Parse
const ast = parser.parse(code, { sourceType: 'module' });
// 2. Transform
traverse(ast, {
ArrowFunctionExpression(path) {
console.log('Found arrow function at', path.node.loc.start.line);
}
});
// 3. Generate
const output = generate(ast, {}, code);
console.log(output.code);