Babel 插件中 "Visitor.Program.enter()" 和 "pre()" 有什么区别?

What's the difference between "Visitor.Program.enter()" and "pre()" in a Babel plugin?

这个 Babel 插件:

module.exports = function(){
    return {
        visitor:{   
            Program:{
                enter(){ console.log('Enter') },
                exit(){ console.log('Exit') }
            }
        },
        pre(){ console.log('Pre') },
        post(){ console.log('Post') }
    }
}

为任何 javascript 文件生成此输出:

Pre
Enter
Exit
Post

pre()Program.enter() 之前被调用,post()Program.exit() 之后被调用。

如果我想 运行 在 AST 遍历的 beginning/end 处的一些代码,是否有任何理由我应该将该代码放在 pre/postProgram.enter/Program.exit?

这有什么区别吗?

AFAIK 没有区别。都叫before/after语法树已经遍历完毕

唯一的区别是传递给 Program.enter/Program.exit 的参数与传递给 pre/post 的参数不同。

module.exports = function(){
    return {
        visitor:{   
            Program:{
                enter(path, state){
                    //path.node
                    //path.parent
                    //state.opts
                },
            }
        },
        pre(state){
            //state.scope
            //state.scope.globals
            //state.scope.plugins
        },
    }
}

例如,从 Program.enter() 您可以使用插件选项访问 state.opts,而从 pre() 您不能。

prepost 有更基本的用途:它们 运行 用于所有插件 before/after 所有遍历。所有插件的顺序是:

  1. pre 运行 所有插件
  2. visitor 运行 所有插件
  3. post 运行 所有插件。

回答你的问题:visitor.Program.enterpre 在大多数情况下表现相同,也就是说,如果你不关心其他插件是否已经开始访问 Program您自己的插件 visitor 开始的时间。主要区别可以概括为两点:

    任何插件开始遍历之前
  1. pre被确保运行。
  2. pre 确保仅 运行 一次 ,而节点访问者可以 运行 多次,因为通过(您自己的或其他插件的)访问者可能会保证无限量的重新访问。

插件(和预设)的执行顺序

请注意,插件的执行顺序是一个被广泛讨论的开放性问题 (see here for a first introduction),prepost 有助于减轻插件可能存在的一些痛苦可以与其他插件冲突。

供参考,这是 Babel7 中插件和预设的执行顺序(当 运行 和下面给出的 babel.config.js 时):

[PLUGIN] pre plugin1
[PLUGIN] pre plugin2
[PLUGIN] pre pres2
[PLUGIN] pre pres1
[PLUGIN] Program plugin1
[PLUGIN] Program plugin2
[PLUGIN] Program pres2
[PLUGIN] Program pres1
[PLUGIN] post plugin1
[PLUGIN] post plugin2
[PLUGIN] post pres2
[PLUGIN] post pres1

参考babel.config.js:

function makeReporterPlugin(msg) {
  return () => {
    return {
      pre() {
        console.log('[PLUGIN] pre', msg);
      },
      visitor: {
        Program() {
          console.log('[PLUGIN] Program', msg);
        }
      },
      post() {
        console.log('[PLUGIN] post', msg);
      },
    };
  };
}

const pres1 = {
  plugins: [
    makeReporterPlugin('pres1')
  ]
};
const pres2 = {
  plugins: [
    makeReporterPlugin('pres2')
  ]
};

const plugin1 = makeReporterPlugin('plugin1');
const plugin2 = makeReporterPlugin('plugin2');

module.exports = {
  "presets": [
    pres1,
    pres2
  ],
  "plugins": [
    plugin1,
    plugin2
  ]
};

讨论:Babel插件执行顺序混乱

当我写我的第一个插件时,我自己也很困惑。 @babel/preset-env之后好像是运行,虽然according to the docs on plugin ordering, presets should run after plugins. However, as explained here,其实没那么简单:所有的plugins'和presets'visitors并行遍历,而描述的顺序在文档中(预设之前的插件)仅针对每个节点单独确保,而不是针对整个 AST 遍历。这个痛点是 prepost 的主要动机。