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
/post
中Program.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()
您不能。
pre
和 post
有更基本的用途:它们 运行 用于所有插件 before/after 所有遍历。所有插件的顺序是:
pre
运行 所有插件
visitor
运行 所有插件
post
运行 所有插件。
回答你的问题:visitor.Program.enter
和 pre
在大多数情况下表现相同,也就是说,如果你不关心其他插件是否已经开始访问 Program
您自己的插件 visitor
开始的时间。主要区别可以概括为两点:
在任何插件开始遍历之前pre
被确保运行。
pre
确保仅 运行 一次 ,而节点访问者可以 运行 多次,因为通过(您自己的或其他插件的)访问者可能会保证无限量的重新访问。
插件(和预设)的执行顺序
请注意,插件的执行顺序是一个被广泛讨论的开放性问题 (see here for a first introduction),pre
和 post
有助于减轻插件可能存在的一些痛苦可以与其他插件冲突。
供参考,这是 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 遍历。这个痛点是 pre
和 post
的主要动机。
这个 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
/post
中Program.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()
您不能。
pre
和 post
有更基本的用途:它们 运行 用于所有插件 before/after 所有遍历。所有插件的顺序是:
pre
运行 所有插件visitor
运行 所有插件post
运行 所有插件。
回答你的问题:visitor.Program.enter
和 pre
在大多数情况下表现相同,也就是说,如果你不关心其他插件是否已经开始访问 Program
您自己的插件 visitor
开始的时间。主要区别可以概括为两点:
-
在任何插件开始遍历之前
pre
被确保运行。pre
确保仅 运行 一次 ,而节点访问者可以 运行 多次,因为通过(您自己的或其他插件的)访问者可能会保证无限量的重新访问。
插件(和预设)的执行顺序
请注意,插件的执行顺序是一个被广泛讨论的开放性问题 (see here for a first introduction),pre
和 post
有助于减轻插件可能存在的一些痛苦可以与其他插件冲突。
供参考,这是 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 遍历。这个痛点是 pre
和 post
的主要动机。