本文详解 Babel 插件开发中如何高效访问抽象语法树节点。涵盖通过 path.node 和 get 方法获取属性,利用内置工具函数进行类型校验与属性匹配,以及使用 findParent、getSibling 等 API 导航节点关系。同时介绍 inList 判断同级节点及 skip、stop 方法优化遍历性能,助力开发者编写高性能 Babel 插件。
访问节点
获取子节点的Path:
在处理节点属性之前,必须先获取节点对象以执行操作。可以通过 path.node.property 直接访问特定属性。
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
此外,还可以利用 path 内置的 get 函数,通过指定属性名来获取对应的属性值。
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
检查节点的类型:
验证节点类型时,可以使用内置的工具类函数 isXxx()。
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
在检查类型的同时,还可以进一步验证某些属性是否符合预期值。
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
// 简化前的代码
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
检查路径(Path)类型:
路径对象提供了与节点类型检查相同的方法,可用于判断路径所代表的节点类型。
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
// 等价于
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
检查标识符(Identifier)是否被引用:
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
// 或者
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
找到特定的父路径:
若需向上查找特定类型的节点,可以使用相关 API 进行定位。
path.findParent((path) => path.isObjectExpression());
如果还需要遍历当前节点及其子树,可结合其他遍历方法使用。
path.find((path) => path.isObjectExpression());
查找最接近的父函数或程序:
path.getFunctionParent();
向上遍历语法树,直至找到位于指定列表中的父节点路径。
path.getStatementParent();
获取同级路径:
若路径位于 Function 或 Program 内部的列表中,则存在同级节点。
- 使用
path.inList来判断路径是否存在同级节点。 - 使用
path.getSibling(index)来获取指定索引的同级路径。 - 使用
path.key获取当前路径在容器中的索引位置。 - 使用
path.container获取包含所有同级节点的容器数组。 - 使用
path.listKey获取容器对应的 key 值。
上述 API 常用于 babel-minify 项目中使用的 transform-merge-sibling-variables 插件。
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
停止遍历:
当遍历达到预期目的后,应尽早结束遍历以避免不必要的性能消耗。
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
如果在顶级路径中进行子遍历,可以使用两个提供的 API 方法来控制流程。
path.skip() 用于跳过当前路径的子路径遍历。
path.stop() 用于完全停止整个遍历过程。
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
以上就是 Babel 插件开发中访问节点的实例详解,更多关于 Babel 插件开发访问节点的资料请关注本站其它相关文章!