如何编写 Babel 插件

1. 面试题

Q: 如果让你写一个 Babel 插件,大概结构是什么样的?Visitor 模式是什么?

2. 核心解答

Babel 插件本质上就是一个 Transformer (代码转换器)。 它利用 AST,遵循 Visitor 模式 (Visitor Pattern),遍历并修改代码。

(1) 插件结构

一个标准 Babel 插件必须是一个函数,返回一个对象。

module.exports = function({ types: t }) {
  return {
    visitor: {
      // 监听 Identifier 类型的节点
      Identifier(path) {
        // 如果变量名是 "n",改成 "x"
        if (path.node.name === 'n') {
          path.node.name = 'x';
        }
      },
      // 监听二元表达式
      BinaryExpression(path) {
        if (path.node.operator !== "===") return;
        path.node.left = t.identifier("sebmck"); // 修改左侧节点
        path.node.right = t.identifier("dork"); // 修改右侧节点
      }
    }
  };
};

(2) Visitor 模式

Babel Traverse 会深度优先遍历 AST 树。当遇到特定的 节点类型 (Node Type) 时,就会调用在这里定义的同名函数 (如 Identifier, BinaryExpression)。

  • path (路径):这是最重要的参数。它不仅包含当前节点 (path.node),还包含了上下文信息 (如父级 path, 作用域 scope),以及操作 AST 的方法 (path.replaceWith, path.remove, path.insertBefore)。
  • state (状态):用于在不同节点之间共享数据。

(3) types (工具库)

插件函数的参数里提供了一个 types 对象 (通常简写为 t)。它类似于 React createElement,用于 创建新的 AST 节点,或者 判断节点类型 (t.isIdentifier(node) )。

3. 面试加分项

Q: 如何判断变量是否被重新赋值?

利用 path.scope (作用域) 对象。Babel 会自动分析作用域链。 path.scope.getBinding('a').constant 为 true 表示常量,false 表示被修改过。这在做常量折叠 (Constant Folding) 优化时非常有用。

4. 总结

  • Visitor: Traverse AST via named methods.
  • Path: Use for context, manipulation.
  • Types: Utility for node creation/checking.