
babel 是一个 JavaScript 编译器,使用 babel 可以随心所欲地转化和操作 AST,实现对代码的分析、优化、变更等。可以在 https://esprima.org/demo/parse.html 体验转换查看 js 代码的词法、语法和AST。

babel操作AST的流程如下图所示,主要分为三步:
@babel/parser 库将js代码解析为AST抽象语法树@babel/traverse 库遍历并修改AST,得到新的AST@babel/generator 库将新的AST生成为js代码,实现代码的转换
以下案例演示了使用 babel 修改箭头函数为普通函数:
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
// 1. 将代码解析为 AST
const code = `const sum = (a, b) => a + b;`;
const ast = parser.parse(code, {
sourceType: 'module', // 或 'script'
plugins: ['jsx', 'typescript']
});
// 2. 遍历 AST
traverse(ast, {
// 访问所有 Identifier 节点
Identifier(path) {
console.log(`找到标识符: ${path.node.name}`);
},
// 访问箭头函数表达式
ArrowFunctionExpression(path) {
// 修改箭头函数为普通函数
path.replaceWith(
t.functionExpression(
null, // 匿名函数
path.node.params,
t.blockStatement([t.returnStatement(path.node.body)])
)
);
}
});
// 3. 生成代码(需 @babel/generator)
const { code: transformedCode } = require('@babel/generator').default(ast);
console.log(transformedCode);
// 输出: const sum = function(a, b) { return a + b; };babel 的 @babel/traverse 模块提供了几个核心方法用于遍历和操作 AST:
traverse(ast, visitors): 这是最基础的AST遍历方法,它接受一个 AST 对象和一个访问器,开发者可以通过开发访问器实现操作各类节点path.traverse(visitors): 用于在遍历过程中(访问器内部)继续遍历和操作当前节点的子节点path.replaceWith(node): 用于在遍历过程中(访问器内部)替换当前节点为指定节点path.remove(): 用于在遍历过程中(访问器内部)删除当前节点及其子节点path.skip(): 用于在访问器内部跳过当前节点的子节点的遍历,避免深度遍历以提高效率path.stop(): 用于停止整个访问器的遍历上面 traverse 的 visitors 参数是用于遍历 AST 的一种特殊的访问器参数,它有两类遍历方式:
traverse(ast, {
enter(path) {
// 进入任何节点时触发
},
exit(path) {
// 离开任何节点时触发
}
});traverse(ast, {
Identifier(path, state) { // 节点类型
// path 提供了许多有用的属性和方法来操作和查询 AST 节点
// state 对象是一个可选的共享状态对象,用于在遍历过程中跨节点传递数据
}
});Babel 的 @babel/traverse 支持遍历所有 Babel AST 节点类型,这些节点类型与 @babel/types 包中定义的 AST 节点类型完全对应。以下是完整的分类列表:
类型名 | 说明 | 示例 |
|---|---|---|
| 标识符 |
|
| 字符串字面量 |
|
| 数字字面量 |
|
| 布尔字面量 |
|
|
|
|
| 正则表达式字面量 |
|
| BigInt 字面量 |
|
| Decimal 字面量 |
|
类型名 | 说明 | 示例 |
|---|---|---|
| 数组表达式 |
|
| 对象表达式 |
|
| 函数表达式 |
|
| 箭头函数表达式 |
|
| 类表达式 |
|
| 函数调用 |
|
|
|
|
| 成员访问 |
|
| 可选链成员访问 |
|
| 赋值表达式 |
|
| 逻辑表达式 |
|
| 二元运算表达式 |
|
| 一元运算表达式 |
|
| 更新表达式 |
|
| 三元条件表达式 |
|
| 模板字符串 |
|
| 标签模板字符串 |
|
| 逗号分隔的表达式序列 |
|
|
|
|
|
|
|
| 动态 |
|
| 可选链整体表达式 |
|
类型名 | 说明 | 示例 |
|---|---|---|
| 表达式语句 |
|
| 代码块 |
|
| 空语句 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 标签语句 |
|
|
|
|
|
|
|
|
|
|
类型名 | 说明 | 示例 |
|---|---|---|
| 变量声明 |
|
| 函数声明 |
|
| 类声明 |
|
|
|
|
|
|
|
| 具名导出声明 |
|
| 默认导出声明 |
|
| 全部导出声明 |
|
| TypeScript 接口声明 |
|
| TypeScript 类型别名 |
|
类型名 | 说明 | 示例 |
|---|---|---|
|
|
|
|
|
|
| 展开元素 |
|
| 剩余参数 |
|
| 元属性 |
|
| JSX 元素 |
|
| JSX 片段 |
|
| JSX 属性 |
|
| JSX 展开属性 |
|
| JSX 表达式容器 |
|
| JSX 文本 |
|
| JSX 空表达式 |
|
| TS 类型断言 |
|
| TS 非空断言 |
|
类型名 | 说明 | 示例 |
|---|---|---|
| 具名导入 |
|
| 默认导入 |
|
| 命名空间导入 |
|
| 具名导出 |
|
类型名 | 说明 |
|---|---|
| TS 类型注解 |
| 数组类型 |
| 联合类型 |
| 交叉类型 |
| 类型引用 |
| 字面量类型 |
babel 的 visitor path 对象是在 AST 遍历过程中传递给 visitor 函数的参数,它提供了许多有用的属性和方法来操作和查询 AST 节点。
属性 | 说明 |
|---|---|
| 当前 AST 节点 |
| 当前节点的直接父节点(AST 对象) |
| 父节点对应的 |
| 若当前节点在数组或对象中,表示包裹它的容器(如数组的父节点) |
| 当前节点在父节点中的属性名(如 |
| 若当前节点在数组中,表示数组的键名(如 |
| 当前节点的作用域( |
| Babel 的全局共享对象(包含配置、元数据等) |
| 遍历的上下文信息(如是否在函数、循环中) |
| 用于存储插件自定义数据的对象 |
| 当前节点的类型(等同于 |
| 传递给插件的配置选项(来自 |
| 遍历过程中的共享状态(可用于跨插件传递数据) |
| 标记当前节点是否已被移除 |
1. 查询与遍历
方法 | 说明 |
|---|---|
| 获取子节点的 |
| 获取数组中相邻节点的 |
| 递归获取祖先路径(可指定最大层级) |
| 从父路径开始查找符合回调条件的路径 |
| 从当前路径开始查找符合回调条件的路径 |
| 检查当前路径是否是另一个路径的祖先 |
| 检查当前路径是否是另一个路径的后代 |
| 检查当前节点是否在数组中(结合 |
| 获取多个路径的最近公共祖先路径 |
2. 节点操作
方法 | 说明 |
|---|---|
| 用新节点替换当前节点 |
| 用多个节点替换当前节点(适用于数组容器) |
| 在当前节点前插入节点 |
| 在当前节点后插入节点 |
| 移除当前节点 |
| 在数组容器的开头插入节点(如函数体的 |
| 在数组容器的末尾插入节点 |
| 用源代码字符串替换当前节点(自动解析为 AST) |
3. 作用域操作
方法 | 说明 |
|---|---|
| 生成唯一的标识符(避免命名冲突) |
| 重命名当前作用域内的变量绑定 |
| 检查当前作用域是否存在某个变量绑定 |
| 获取变量的绑定信息(包括声明、引用等) |
| 仅检查当前作用域自身(不包含父级)的绑定 |
| 向作用域内添加新的绑定(手动操作) |
| 移除作用域内的某个绑定 |
4. 类型检查
方法 | 说明 |
|---|---|
| 一系列类型检查方法(如 |
| 断言当前节点类型(失败会抛错,如 |
| 检查节点是否匹配特定的对象模式(如 |
5. 流程控制
方法 | 说明 |
|---|---|
| 跳过当前节点的子节点遍历 |
| 停止整个遍历过程 |
| 在修改 AST 后重新同步遍历状态(用于手动修复路径) |
6. 代码生成与错误
方法 | 说明 |
|---|---|
| 生成包含源代码位置信息的错误对象(用于插件报错) |
7. 高级工具
方法 | 说明 |
|---|---|
| 将当前节点提升到父级作用域(用于变量或函数提升) |
| 手动设置当前路径的作用域 |
| 获取所有绑定标识符的路径(用于分析变量引用) |
| 获取外层作用域的绑定标识符路径 |
isJSXElement(), getJSXAttribute(), buildJSXElement() 等。getTypeAnnotation(), setTypeAnnotation()(用于 Flow/TypeScript)。getTemplateLiteralElements().node 属性:推荐使用 replaceWith 或 insertBefore 等方法,以确保 AST 的完整性。scope 方法操作,避免破坏作用域链。skip() 或条件判断减少不必要的遍历。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。