ホームページ  >  記事  >  ウェブフロントエンド  >  babel はどのように es6 クラス構文を変換しますか

babel はどのように es6 クラス構文を変換しますか

php中世界最好的语言
php中世界最好的语言オリジナル
2018-04-08 11:34:412171ブラウズ

今回はBabelがes6のクラス構文を変換する方法を紹介します。 以下は実際的なケースです。 babel はトランスコーダーであり、現在、react および vue プロジェクトの開発時に使用されています。 es6+ 構文を es5 に変換できるほか、JSX やその他の構文も変換できます。

私たちのプロジェクトでは、プラグインやプリセット (複数のプラグインのコレクション) を構成することで、env、stage-0 などの特定のコードを変換します。

実際、Babel はカスタム プラグインを通じてあらゆるコードを変換できます。次に、「es6 の class を es5 に変換する」例を通して Babel について学びましょう。

内容は次のとおりです: class 转换为es5”的例子来了解一下babel。

内容如下:

webpack环境配置

大家应该都配置过babel-core这个loader,它的作用是提供babel的核心Api,实际上我们的代码转换都是通过插件来实现的。

接下来我们不用第三方的插件,自己实现一个es6类转换插件。先执行以下几步初始化一个项目:

  1. npm install webpack webpack-cli babel-core -D

  2. 新建一个webpack.config.js

  3. 配置webpack.config.js

如果我们的插件名字想叫transform-class,需要在webpack配置中做如下配置:

接下来我们在node_modules中新建一个babel-plugin-transform-class的文件夹来写插件的逻辑(如果是真实项目,你需要编写这个插件并发布到npm仓库),如下图:

红色区域是我新建的文件夹,它上面的是一个标准的插件的项目结构,为了方便我只写了核心的index.js文件。

如何编写bable插件

babel插件其实是通过AST(抽象语法树)实现的。

babel帮助我们把js代码转换为AST,然后允许我们修改,最后再把它转换成js代码。

那么就涉及到两个问题:js代码和AST之间的映射关系是什么?如何替换或者新增AST?

好,先介绍一个工具:astexplorer.net:

这个工具可以把一段代码转换为AST:

如图,我们写了一个es6的类,然后网页的右边帮我们生成了一个AST,其实就是把每一行代码变成了一个对象,这样我们就实现了一个映射。

再介绍一个文档: babel-types :

这是创建AST节点的api文档。

比如,我们想创建一个类,先到astexplorer.net中转换,发现类对应的AST类型是 ClassDeclaration 。好,我们去文档中搜索,发现调用下面的api就可以了:

创建其他语句也是一样的道理,有了上面这两个东西,我们可以做任何转换了。

下面我们开始真正编写一个插件,分为以下几步:

  1. 在index.js中export一个函数

  2. 函数中返回一个对象,对象有一个visitor参数(必须叫visitor)

  3. 通过astexplorer.net查询class 对应的AST节点为 ClassDeclaration

  4. 在vistor中设置一个捕获函数 ClassDeclaration ,意思是我要捕获js代码中所有 ClassDeclaration

    webpack 環境設定
  5. 使用される babel-core ローダーは全員が設定しているはずです。 Babel のコア API を提供するために、実際、コード変換はプラグインを通じて実装されています。

    次に、サードパーティのプラグインを使用せずに、es6変換プラグインを自分で実装します。まず、次の手順を実行してプロジェクトを初期化します。 🎜
      🎜🎜npm install webpack webpack-cli babel-core -D🎜🎜🎜🎜 新しい webpack.config.js を作成します🎜🎜 🎜🎜 webpack.config.js を設定します🎜🎜
    🎜 プラグイン名をtransform-classと呼びたい場合は、webpack設定で次の設定を行う必要があります: 🎜🎜🎜🎜次に、書き込むためにnode_modulesにbabel-plugin-transform-classの新しいフォルダーを作成します。以下に示すように、プラグインのロジック (実際のプロジェクトの場合、このプラグインを作成して npm リポジトリに公開する必要がある場合): 🎜🎜🎜🎜赤い部分は私が作成したフォルダーです。その上にあるのは標準のプラグイン プロジェクト構造です。便宜上、コアのindex.jsファイルのみを作成しました。 🎜🎜babel プラグインの書き方🎜🎜babel プラグインは実際には AST (抽象構文ツリー) を通じて実装されます。 🎜🎜babel は、js コードを AST に変換するのに役立ち、それを変更して、最後に js コードに変換することを可能にします。 🎜🎜それでは 2 つの質問が関係しています: js コードと AST の間のマッピング関係は何ですか? 🎜AST を置換または追加するにはどうすればよいですか? 🎜🎜それでは、まずツールを紹介しましょう: astexplorer.net:🎜🎜このツールは、コードの一部を AST:🎜🎜🎜🎜図に示すように、Web ページの右側で AST を生成し、実際にそれぞれを回転させました。コード行を オブジェクト 🎜 に変換するため、マッピングを実装します。 🎜🎜別のドキュメントの紹介: babel-types:🎜🎜これは、AST ノードを作成するための API ドキュメントです。 🎜🎜たとえば、クラスを作成したい場合、まずそれを astexplorer.net で変換し、そのクラスに対応する AST タイプが ClassDeclaration であることを確認します。さて、ドキュメントを検索して、次の API を呼び出すだけで十分であることがわかります: 🎜🎜🎜🎜他のステートメントを作成する場合も同様です。上記の 2 つがあれば、任意の変換を行うことができます。 🎜🎜ここで実際にプラグインを書き始めます。これは次のステップに分かれています: 🎜
      🎜🎜index.js で関数をエクスポートします🎜🎜🎜🎜関数は戻りますオブジェクト、オブジェクト 訪問者パラメータがあります (訪問者と呼ばれる必要があります)🎜🎜🎜🎜 astexplorer.net🎜 class の対応する AST ノードは ClassDeclaration です🎜🎜🎜🎜 キャプチャ関数 ClassDeclaration をビジターに設定します。すべての ClassDeclaration ノード🎜🎜🎜🎜変換を完了するためのロジック コードを作成します🎜
module.exports = function ({ types: t }) {
 return {
  visitor: {
   ClassDeclaration(path) {
    //在这里完成转换
   }
  }
 };
}

コードには 2 つのパラメータがあります。最初の {types:t} はパラメータ 変数 {types:t} 东西是从参数中解构出变量t,它其实就是babel-types文档中的t(下图红框),它是用来创建节点的:

第二个参数 path ,它是捕获到的节点对应的信息,我们可以通过 path.node 获得这个节点的AST,在这个基础上进行修改就能完成了我们的目标。

如何把es6的class转换为es5的类

上面都是预备工作,真正的逻辑从现在才开始,我们先考虑两个问题:

我们要做如下转换,首先把es6的类,转换为es5的类写法(也就是普通函数),我们观察到,很多代码是可以复用的,包括函数名字、函数内部的代码块等。

 

如果不定义class中的 constructor 方法,JavaScript引擎会自动为它添加一个空的 constructor() 方法,这需要我们做兼容处理。

接下来我们开始写代码,思路是:

  1. 拿到老的AST节点

  2. 创建一个数组用来盛放新的AST节点(虽然原class只是一个节点,但是替换后它会被若干个函数节点取代) 初始化默认的 constructor 节点(上文提到,class中有可能没有定义constructor)

  3. 循环老节点的AST对象(会循环出若干个函数节点)

  4. 判断函数的类型是不是 constructor ,如果是,通过取到数据创建一个普通函数节点,并更新默认 constructor 节点

  5. 处理其余不是 constructor 的节点,通过数据创建 prototype 类型的函数,并放到 es5Fns

  6. 循环结束,把 constructor 节点也放到 es5Fns

  7. 判断es5Fns的长度是否大于1,如果大于1使用 replaceWithMultiple 这个API更新AST

module.exports = function ({ types: t }) {
 return {
  visitor: {
   ClassDeclaration(path) {
    //拿到老的AST节点
    let node = path.node
    let className = node.id.name
    let classInner = node.body.body
    //创建一个数组用来成盛放新生成AST
    let es5Fns = []
    //初始化默认的constructor节点
    let newConstructorId = t.identifier(className)
    let constructorFn = t.functionDeclaration(newConstructorId, [t.identifier('')], t.blockStatement([]), false, false)
    //循环老节点的AST对象
    for (let i = 0; i < classInner.length; i++) {
     let item = classInner[i]
     //判断函数的类型是不是constructor
     if (item.kind == &#39;constructor&#39;) {
      let constructorParams = item.params.length ? item.params[0].name : []
      let newConstructorParams = t.identifier(constructorParams)
      let constructorBody = classInner[i].body
      constructorFn = t.functionDeclaration(newConstructorId, [newConstructorParams], constructorBody, false, false)
     } 
     //处理其余不是constructor的节点
     else {
      let protoTypeObj = t.memberExpression(t.identifier(className), t.identifier(&#39;prototype&#39;), false)
      let left = t.memberExpression(protoTypeObj, t.identifier(item.key.name), false)
      //定义等号右边
      let prototypeParams = classInner[i].params.length ? classInner[i].params[i].name : []
      let newPrototypeParams = t.identifier(prototypeParams)
      let prototypeBody = classInner[i].body
      let right = t.functionExpression(null, [newPrototypeParams], prototypeBody, false, false)
      let protoTypeExpression = t.assignmentExpression("=", left, right)
      es5Fns.push(protoTypeExpression)
     }
    }
    //循环结束,把constructor节点也放到es5Fns中
    es5Fns.push(constructorFn)
    //判断es5Fns的长度是否大于1
    if (es5Fns.length > 1) {
     path.replaceWithMultiple(es5Fns)
    } else {
     path.replaceWith(constructorFn)
    }
   }
  }
 };
}

优化继承

其实,类还涉及到继承,思路也不复杂,就是判断AST中没有 superClass 属性,如果有的话,我们需要多添加一行代码 Bird.prototype = Object.create(Parent) ,当然别忘了处理 super 关键字。

打包后代码

 

运行 npm start 打包后,我们看到打包后的文件里 classt。これは実際には babel-types ドキュメント内の t (下の図の赤いボックス) であり、ノードの作成に使用されます:

2 番目のパラメータ path は、対応するキャプチャされたノード情報です。 path.node を通じてこのノードの AST を取得し、これに基づいて変更して目的を達成できます。

es6 クラスを es5 クラスに変換する方法

上記はすべて準備作業であり、本当のロジックはこれから始まります。2 つ考えてみましょう。まず問題:

まず、es6 クラスを es5 クラスの記述 (つまり、通常の関数) に変換する必要があります。関数名や関数内のコード ブロックなど、多くのコードが再利用できることがわかりました。 、など。

を定義しない場合クラス コンストラクター メソッド、JavaScript

エンジンは空の constructor( ) メソッドでは、互換性処理を行う必要があります。 <a href="http://www.php.cn/js-tutorial-391738.html" target="_blank"></a>次に、コードの作成を開始します。アイデアは次のとおりです。<br><ol class=" list-paddingleft-2"><li></ol>

古い AST ノードを取得する🎜🎜
  • 🎜 新しい AST ノードを保持する配列を作成する(元のクラスは1つのノードですが、置換後は複数の関数ノードに置き換えられます) デフォルトのconstructorノードを初期化します(前述のように、クラスにコンストラクターが定義されていない可能性があります)🎜🎜
  • 🎜古いノードの AST オブジェクトをループします (いくつかの関数ノードがループアウトされます)🎜🎜
  • 🎜 関数の型が constructor であるかどうかを判断します。そうであれば、通常の関数を作成します。 1 つはデータ Function ノードを取得し、デフォルトの constructor ノードを更新します 🎜🎜
  • 🎜 constructor ではない残りのノードを処理し、 の関数を作成します>prototype はデータを介して入力し、それを es5Fns に配置します🎜🎜
  • 🎜ループが終了し、constructor ノードを es5Fns> に配置します。 code>🎜🎜<li>🎜判定 es5Fns の長さは 1 より大きいか? 1 より大きい場合は、<code>replaceWithMultiple を使用して AST🎜🎜🎜rrreee🎜継承の最適化🎜🎜実際、クラスにも継承が含まれており、考え方は複雑ではありません。superClass が存在しないことを判断することです。 AST に属性がある場合は、コード Bird.prototype = Object.create(Parent) をもう 1 行追加する必要があります。もちろん、super を処理することを忘れないでください。 キーワード。 🎜🎜パッケージ化されたコード🎜🎜 🎜🎜 npm start を実行します。 パッケージ化後、パッケージ化されたファイル内の class🎜🎜 構文が 1 つずつ es5 関数に正常に変換されていることがわかります。 🎜🎜🎜🎜この記事の事例を読んだ後は、この方法を習得したと思います。さらに興味深い情報については、php 中国語 Web サイトの他の関連記事に注目してください。 🎜🎜推奨読書: 🎜🎜🎜VueプロジェクトにTencent認証コード機能を導入する方法🎜🎜🎜🎜🎜JSでタイマー+プロンプトボックスを実装する🎜🎜🎜
  • 以上がbabel はどのように es6 クラス構文を変換しますかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。