Javascript react intl的Babel插件开发
我注意到,在比较Javascript react intl的Babel插件开发,javascript,babeljs,react-intl,babel-core,Javascript,Babeljs,React Intl,Babel Core,我注意到,在比较intl.formatMessage({id:'section.someid'})vsintl.messages['section.someid']之后,react intl有一些性能提升的机会。 请参阅此处的更多信息: 第二种方法速度快了5倍(并且在包含大量翻译元素的页面中产生了巨大的差异),但这似乎不是正式的方法(我想他们可能会在未来的版本中更改变量名) 所以我想创建一个babel插件来进行转换(formatMessage(to messages[))。但是我很难做到这一点,
intl.formatMessage({id:'section.someid'})
vsintl.messages['section.someid']
之后,react intl有一些性能提升的机会。
请参阅此处的更多信息:
第二种方法速度快了5倍(并且在包含大量翻译元素的页面中产生了巨大的差异),但这似乎不是正式的方法(我想他们可能会在未来的版本中更改变量名)
所以我想创建一个babel插件来进行转换(formatMessage(to messages[))。但是我很难做到这一点,因为babel插件的创建没有很好的文档记录(我找到了一些教程,但它没有我需要的内容)。我了解基本知识,但还没有找到我需要的访问者函数名
我的样板代码当前为:
module.exports=函数(巴别塔){
var t=巴别塔类型;
返回{
参观者:{
CallExpression(路径、状态){
console.log(路径);
},
}
};
};
下面是我的问题:
- 我使用哪个访问者方法提取类调用-intl.formatMessage(它真的是CallExpression吗)
- 如何检测对formatMessage的调用
- 如何检测调用中的参数数量?(如果存在格式设置,则不应进行替换)
- 如何替换?(intl.formatMessage({id:'something'})到intl.messages['something'])
- (可选)是否有方法检测formatMessage是否真的来自react intl库
调用表达式
,与函数调用相比,方法调用没有特殊的AST节点,唯一改变的是接收方(被调用方)。当你想知道AST是什么样子的时候,你可以使用奇妙的。作为奖励,你甚至可以通过在转换菜单中选择Babel,在AST浏览器中编写Babel插件
如何检测对formatMessage的调用
为简洁起见,我将只关注对intl.formatMessage(arg)
的确切调用,对于一个真正的插件,您还需要涵盖具有不同AST表示的其他情况(例如intl[“formatMessage”](arg)
)
第一件事是确定被调用方是intl.formatMessage
。正如您所知,这是一个简单的对象属性访问,对应的AST节点称为MemberExpression
。访问者接收匹配的AST节点,CallExpression
。在本例中,作为path.node
。这意味着我们需要确认path.node.callee
是MemberExpression
。谢天谢地,这非常简单,因为babel.types
以isX
的形式提供了方法,其中X
是AST节点类型
if (t.isMemberExpression(path.node.callee)) {}
现在我们知道它是一个成员表达式
,它有一个对象
和一个属性
,对应于对象.属性
。因此我们可以检查对象
是否是标识符intl
和属性
标识符格式消息
。为此,我们使用isIdentifier(节点,选项)
,它接受第二个参数,允许您检查它是否具有具有给定值的属性。所有isX
方法都是这种形式,以提供快捷方式,有关详细信息,请参阅。它们还检查节点是否不为null
或未定义
,因此isMemberExpression
在技术上是不必要的,但您可能希望以不同的方式处理另一种类型
if (
t.isIdentifier(path.node.callee.object, { name: "intl" }) &&
t.isIdentifier(path.node.callee.property, { name: "formatMessage" })
) {}
如何检测调用中的参数数量?(如果存在格式设置,则不应进行替换)
<> >代码>调用表达式< /代码>具有<代码>参数<代码>属性,它是参数的AST节点的数组。同样,为了简洁起见,我只考虑调用一个参数,但实际上,您也可以转换一些类似于路径.node.arguments
的长度。我们还希望参数是一个对象,因此我们检查对象表达式
if (
path.node.arguments.length === 1 &&
t.isObjectExpression(path.node.arguments[0])
) {}
ObjectExpression
有一个properties
属性,它是ObjectProperty
节点的数组。从技术上讲,您可以检查id
是否是唯一的属性,但我将在这里跳过它,而只查找id
属性。ObjectProperty
有一个键和值
,我们可以使用来搜索键为标识符id的属性
const idProp = path.node.arguments[0].properties.find(prop =>
t.isIdentifier(prop.key, { name: "id" })
);
idProp
将是相应的ObjectProperty
(如果它存在),否则它将是未定义的
。如果它不是未定义的
,我们希望替换节点
如何替换?(intl.formatMessage({id:'something'})到intl.messages['something'])
我们想替换整个CallExpression
,Babel提供了它。剩下的唯一一件事就是创建它应该替换的AST节点。为此,我们首先需要了解intl.messages[“section.someid”]
在AST中表示。intl.messages
是MemberExpression
就像intl.formatMessage
was一样。obj[“属性”]
是一个计算属性对象访问,它在AST中也表示为MemberExpression
,但computed
属性设置为true
。这意味着intl.messages[“section.someid”]
是一个MemberExpression
对象的MemberExpression
请记住,这两者在语义上是等价的:
intl.messages["section.someid"];
const msgs = intl.messages;
msgs["section.someid"];
要构造MemberExpression
,我们可以使用.For c
t.memberExpression(path.node.callee.object, t.identifier("messages"))
if (idProp) {
path.replaceWith(
t.memberExpression(
t.memberExpression(
path.node.callee.object,
t.identifier("messages")
),
idProp.value,
// Is a computed property
true
)
);
}
export default function({ types: t }) {
return {
visitor: {
CallExpression(path) {
// Make sure it's a method call (obj.method)
if (t.isMemberExpression(path.node.callee)) {
// The object should be an identifier with the name intl and the
// method name should be an identifier with the name formatMessage
if (
t.isIdentifier(path.node.callee.object, { name: "intl" }) &&
t.isIdentifier(path.node.callee.property, { name: "formatMessage" })
) {
// Exactly 1 argument which is an object
if (
path.node.arguments.length === 1 &&
t.isObjectExpression(path.node.arguments[0])
) {
// Find the property id on the object
const idProp = path.node.arguments[0].properties.find(prop =>
t.isIdentifier(prop.key, { name: "id" })
);
if (idProp) {
// When all of the above was true, the node can be replaced
// with an array access. An array access is a member
// expression with a computed value.
path.replaceWith(
t.memberExpression(
t.memberExpression(
path.node.callee.object,
t.identifier("messages")
),
idProp.value,
// Is a computed property
true
)
);
}
}
}
}
}
}
};
}
const idProp = path.node.arguments[0].properties.find(prop =>
t.isIdentifier(prop.key, { name: "id" }) ||
t.isStringLiteral(prop.key, { value: "id" })
);