最佳实践

尽量避免遍历抽象语法树(AST)

遍历 AST 的代价很昂贵,并且很容易做出非必要的遍历,可能是数以千计甚或上万次的多余操作。

Babel 尽可能的对此做出了优化,方法是如果合并多个访问者能够在单次遍历做完所有事情的话那就合并它们。

及时合并访问者对象

当编写访问者时,若逻辑上必要的话,它会试图在多处调用 path.traverse

path.traverse({
  Identifier(path) {
    // ...
  }
});
path.traverse({
  BinaryExpression(path) {
    // ...
  }
});

不过若能把它们写进一个访问者的话会更好,这样只会运行一次,否则你会毫无必要的对同一棵树遍历多次。

path.traverse({
  Identifier(path) {
    // ...
  },
  BinaryExpression(path) {
    // ...
  }
});

可以手动查找就不要遍历

访问者也会尝试在查找一个特定节点类型时调用 path.traverse

const visitorOne = {
  Identifier(path) {
    // ...
  }
};
const MyVisitor = {
  FunctionDeclaration(path) {
    path.get("params").traverse(visitorOne);
  }
};

然而如果你查找的是很明确并且是表层的节点,那么手动去查找它们会避免代价更高的遍历。

const MyVisitor = {
  FunctionDeclaration(path) {
    path.node.params.forEach(function() {
      // ...
    });
  }
};

优化嵌套的访问者对象

当你嵌套访问者时,直接把它们嵌套式的写进代码里看起来很合理。

const MyVisitor = {
  FunctionDeclaration(path) {
    path.traverse({
      Identifier(path) {
        // ...
      }
    });
  }
};

当时上述代码在每次调用 FunctionDeclaration() 时都会创建新的访问者对象,使得 Babel 变得更大并且每次都要去做验证。 这也是代价不菲的,所以最好把访问者向上提升。

const visitorOne = {
  Identifier(path) {
    // ...
  }
};
const MyVisitor = {
  FunctionDeclaration(path) {
    path.traverse(visitorOne);
  }
};

如果你需要嵌套的访问者的内部状态,就像这样:

const MyVisitor = {
  FunctionDeclaration(path) {
    var exampleState = path.node.params[0].name;
    path.traverse({
      Identifier(path) {
        if (path.node.name === exampleState) {
          // ...
        }
      }
    });
  }
};

可以传递给 traverse() 方法的第二个参数然后在访问者中用 this 去访问。

const visitorOne = {
  Identifier(path) {
    if (path.node.name === this.exampleState) {
      // ...
    }
  }
};
const MyVisitor = {
  FunctionDeclaration(path) {
    var exampleState = path.node.params[0].name;
    path.traverse(visitorOne, { exampleState });
  }
};

留意嵌套结构

有时候在考虑一些转换时,你可能会忘记某些结构是可以嵌套的。

举例来说,假设我们要从 Foo ClassDeclaration 中查找 constructor ClassMethod。.

class Foo {
  constructor() {
    // ...
  }
}
const constructorVisitor = {
  ClassMethod(path) {
    if (path.node.name === "constructor") {
      // ...
    }
  }
};
const MyVisitor = {
  ClassDeclaration(path) {
    if (path.node.id.name === "Foo") {
      path.traverse(constructorVisitor);
    }
  }
};

可是我们忽略了类型定义是可以嵌套的,于是使用上面的遍历方式最终也会找到嵌套的 constructor

class Foo {
  constructor() {
    class Bar {
      constructor() {
        // ...
      }
    }
  }
}

MIT Licensed | Copyright © 2018-present 滇ICP备16006294号

Design by Quanzaiyu | Power by VuePress