2017-10-13 30 views
1

intl.formatMessage({ id: 'section.someid' })intl.messages['section.someid']を比較した後に、反応intlのパフォーマンスが向上する可能性があることに気付きました。 もっと見る:https://github.com/yahoo/react-intl/issues/1044react-intlのBabelプラグイン開発

2番目は5倍高速です(翻訳された要素の多いページでは大きな違いがあります)。しかし、それを行う公式な方法ではありません。将来のバージョンでは変数名)。

私は、変換(formatMessage(メッセージへ))を行うbabelプラグインを作成する考えがありましたが、babelプラグインの作成が十分に文書化されていないので「tは、私は基本を理解したが、私はまだ必要な訪問者の関数名を見つけられませんでした)私は必要なものを持っている

私の定型的なコードは、現在、次のとおりです。だからここ

module.exports = function(babel) { 
    var t = babel.types; 
    return { 
    visitor: { 
     CallExpression(path, state) { 
     console.log(path); 
     }, 
    } 
    }; 
}; 

が私の質問です:

  • 私はクラス呼び出しを抽出するためにどのvisitorメソッドを使用しますか? - intl.formatMessage(本当にCallExpressionですか?)
  • formatMessageへの呼び出しを検出するにはどうすればよいですか?
  • コールのパラメータ数を検出するにはどうすればよいですか? (フォーマットが行われている場合、交換は行われません)
  • 交換方法は? (intl.formatMessage({ID: '何か'}?)intl.messages [ '何か']へ
  • (オプション)FORMATMESSAGEが本当に反応し、国際ライブラリーから来ているかどうかを検出する方法はある

答えて

1
?私はクラスを抽出するために使用しないビジター方法

呼び出し - ?intl.formatMessage(本当にそれCallExpressionです)

はい、それはCallExpressionで、メソッド呼び出しのための特別なASTノードがに比べてありません関数呼び出し、変更する唯一のものは受信側(呼び出し先)です。あなたがASTの外観を疑問に思っているときはいつでも、あなたは素晴らしいAST Explorerを使用することができます。ボーナスとして、変形メニューでBabelを選択することでASTエクスプローラでBabelプラグインを作成することもできます。

formatMessageの呼び出しを検出するにはどうすればよいですか?私は別のAST表現を持っているあなたは、他のケースをカバーする必要があるだろう本当のプラグイン(例えばintl["formatMessage"](arg))のために、intl.formatMessage(arg)への正確な呼び出しに焦点を当てる簡潔にするために

最初に、着信先がintl.formatMessageであることを確認します。ご存知のように、これは単純なオブジェクトプロパティアクセスで、対応するASTノードはMemberExpressionと呼ばれます。ビジターは一致するASTノードCallExpressionをこの場合path.nodeとして受信します。つまり、path.node.calleeMemberExpressionであることを確認する必要があります。ありがたいことに、それはbabel.typesisXの形式でメソッドを提供するので、それは非常に単純です。ここで、XはASTノードタイプです。

if (t.isMemberExpression(path.node.callee)) {} 

今、我々はそれがobject.propertyに対応objectpropertyを持ってMemberExpression、だということを知っています。したがって、が識別子intlpropertyの識別子formatMessageであるかどうかを確認できます。このためには、isIdentifier(node, opts)を使用します。これには、指定された値を持つプロパティがあることを確認できる2番目の引数があります。すべてのisXメソッドは、ショートカットを提供する形式です。詳細は、Check if a node is a certain typeを参照してください。また、ノードがnullまたはundefinedでないことを確認するので、技術的には不要ですが、別のタイプを別の方法で処理することもできます。私は、コールのパラメータの数を検出するにはどうすればよい

if (
    t.isIdentifier(path.node.callee.object, { name: "intl" }) && 
    t.isIdentifier(path.node.callee.property, { name: "formatMessage" }) 
) {} 

CallExpression

引数のASTノードのアレイであるarguments性質を有する(交換があっフォーマットされている場合に発生するように想定されていません)。繰り返しになりますが、簡潔にするために、私はちょうど1つの引数で呼び出しを検討しますが、実際にはintl.formatMessage(arg, undefined)のようなものを変換することもできます。この場合、単に長さを確認するのはpath.node.argumentsです。また引数がオブジェクトであることが必要なので、ObjectExpressionをチェックします。

if (
    path.node.arguments.length === 1 && 
    t.isObjectExpression(path.node.arguments[0]) 
) {} 

ObjectExpressionObjectPropertyノードの配列であるproperties性質を有しています。技術的には、idが唯一のプロパティであることを確認できますが、ここではスキップして、代わりにidプロパティを探します。 にはkeyvalueがあり、Array.prototype.find()を使用して、キーが識別子idであるプロパティを検索することができます。

const idProp = path.node.arguments[0].properties.find(prop => 
    t.isIdentifier(prop.key, { name: "id" }) 
); 

idPropが存在する場合、それ以外の場合はundefinedなり、対応ObjectPropertyあろう。 undefinedでない場合は、ノードを置き換えます。

どのように交換しますか? (intl.formatMessage({ID: '何か'}?)intl.messagesには、[ '何か']

我々は全体CallExpressionを交換したいとバベルはpath.replaceWith(node)を提供して残された唯一のものは、ASTを作成しています。その代わりに、intl.messages["section.someid"]がASTでどのように表現されているかを理解する必要があります。intl.messagesMemberExpressionとちょうど同様ですobj["property"]はで表されるASTの計算されたプロパティオブジェクトアクセスです。 computedプロパティがtrueに設定されています。つまり、intl.messages["section.someid"]MemberExpressionで、MemberExpressionがオブジェクト。

は、これら二つの意味的に等価であることを忘れないでください:

intl.messages["section.someid"]; 

const msgs = intl.messages; 
msgs["section.someid"]; 

我々はt.memberExpression(object, property, computed, optional)を使用することができますMemberExpressionを構築します。 intl.messagesを作成するには、intlpath.node.callee.objectから再利用することができます。同じオブジェクトを使用しますが、プロパティを変更します。プロパティの場合は、という名前のmessagesを作成する必要があります。

t.memberExpression(path.node.callee.object, t.identifier("messages")) 

だけ最初の二つの引数を必要とし、残りの我々は、デフォルト値(オプション用computedためfalsenull)を使用しています。今度はMemberExpressionをオブジェクトとして使用することができ、先に計算したidPropで利用可能なidプロパティの値に対応する計算されたプロパティ(3番目の引数はtrueに設定されています)をルックアップする必要があります。最後に、CallExpressionノードを新しく作成したノードに置き換えます。

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 
       ) 
       ); 
       } 
      } 
      } 
     } 
     } 
    } 
    }; 
} 

完全なコードといくつかのテストケースがthis AST Explorer Gistで見つけることができます。

これまで数回述べたことがありますが、これは単純なバージョンであり、多くのケースがカバーされていないため、変換に適しています。より多くのケースをカバーすることは困難ではありませんが、それらを識別してASTエクスプローラに貼り付けると、必要なすべての情報が得られます。オブジェクトが{ "id": "section.someid" }代わりの{ id: "section.someid" }ある場合たとえば、それは、変換されたが、これをカバーすることもIdentifier以外StringLiteralをチェックするのと同じくらい簡単です、このようにされることはありません。

const idProp = path.node.arguments[0].properties.find(prop => 
    t.isIdentifier(prop.key, { name: "id" }) || 
    t.isStringLiteral(prop.key, { value: "id" }) 
); 

私も導入していませんでした付加的な認知負荷を避けるために意図的に任意の抽象化を行うため、条件は非常に長く見える。

参考リソース:

+0

マイケルは、非常に最良の答えありがとうございました。私は今日それを試してみます。 :) –

+0

もう一度ありがとう。あなたが私の問題を解決するのを助けたり、プラグインをする方法を教えたりしなかったかもしれません。私はあなたの回答をここではリアクションintl github問題https://github.com/yahoo/react-intl/issues/1044で共有しました –

関連する問題