<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          AST抽象語法樹和Babel插件

          共 5671字,需瀏覽 12分鐘

           ·

          2020-11-06 06:35

          作者:wl
          來源:SegmentFault 思否



          AST(抽象語法樹,AST)抽象語法樹,可以把代碼轉(zhuǎn)譯成語法樹的表現(xiàn)形式


          例如下面的代碼:

          var a = 3;a + 5

          AST抽象出來的樹結(jié)構(gòu):
          Program代表的是根例程

          • VariableDeclaration變量聲明
            • Identifier標識符+Numeric Literal數(shù)字字面量
          • BinaryExpression(二項式)
            • Identifier標識符,operator二項式運算符,Numeric Literal數(shù)字字面量

          可以到astexplorer.net查看AST的解析結(jié)果




          編譯器過程


          大多數(shù)編譯器的工作過程可以分為三部分:

          • 解析
          • 轉(zhuǎn)換
          • 生成(代碼生成)
          安裝esprima來理解編譯的過程:

          npm install esprima estraverse escodegen
          const esprima = require('esprima')const estraverse = require('estraverse')const escodegen = require('escodegen')
          let code = `var a = 3`
          // Parse(解析)let ast = esprima.parseScript(code);
          //Transform(轉(zhuǎn)換)estraverse.traverse(ast, { enter(node) { console.log("enter",node.type); }, leave(node) { console.log("leave",node.type); }});
          // Generate(代碼生成)const result = escodegen.generate(ast);

          Babel對于AST的遍歷是深度優(yōu)先遍歷,對于AST上的每一個分支Babel都會先向下遍歷走到盡頭,然后再向上遍歷退出剛遍歷過的例程,然后尋找下一個分支。


          AST對語法樹的遍歷是深度優(yōu)先遍歷,所以會先向下遍歷走到盡頭,然后再向上遍歷退出剛遍歷過的隊列,尋找下一個分支,所以遍歷的過程中控制臺會打印下面的信息:

          enter Programenter VariableDeclarationenter VariableDeclaratorenter Identifierleave Identifierenter Literalleave Literalleave VariableDeclaratorleave VariableDeclarationleave Program

          通過type的判斷我們可以修改變量的值:

          estraverse.traverse(ast, {  enter(node) {    if(node.type === "Literal"){      node.value = "change";    }  }});
          // var a = "change";




          babel插件


          來看下babel是如何工作的,首先通過npm安裝@babel/core和babel-types:

          npm install @babel/core

          我們知道babel能編譯es6代碼,例如最基礎(chǔ)的const和箭頭函數(shù):

          // es2015 的 const 和 arrow functionconst add = (a, b) => a + b;
          // Babel 轉(zhuǎn)譯后var add = function add(a, b) { return a + b;};

          我們可以到astexplorer?查看生成的語法樹:

          {  "type": "Program",  "body": [    {      "type": "VariableDeclaration", // 變量聲明      "declarations": [ // 具體聲明        {          "type": "VariableDeclarator", // 變量聲明          "id": {            "type": "Identifier", // 標識符(最基礎(chǔ)的)            "name": "add" // 函數(shù)名          },          "init": {            "type": "ArrowFunctionExpression", // 箭頭函數(shù)            "id": null,            "expression": true,            "generator": false,            "params": [ // 參數(shù)              {                "type": "Identifier",                "name": "a"              },              {                "type": "Identifier",                "name": "b"              }            ],            "body": { // 函數(shù)體              "type": "BinaryExpression", // 二項式              "left": { // 二項式左邊                "type": "Identifier",                "name": "a"              },              "operator": "+", // 二項式運算符              "right": { // 二項式右邊                "type": "Identifier",                "name": "b"              }            }          }        }      ],      "kind": "const"    }  ],  "sourceType": "module"}

          通過代碼模擬一下:

          const babel = require('babel-core');const t = require('babel-types');
          let code = `let add = (a, b)=>{return a+b}`;let ArrowPlugins = {visitor: { ArrowFunctionExpression(path) { let { node } = path; let body = node.body; let params = node.params; let r = t.functionExpression(null, params, body, false, false); path.replaceWith(r); } }}let result = babel.transform(code, { plugins: [ ArrowPlugins ]})console.log(result.code);

          我們可以在訪問者visitor中捕獲到匹配的type,在某些函數(shù)里面替換箭頭函數(shù)。



          類轉(zhuǎn)換


          const babel = require("@babel/core");const typs = require("@babel/types");
          const code = `class Animal { constructor(name){ this.name = name } getName(){ return this.name }}`
          const classPlugins = { visitor:{ ClassDeclaration(path){ let node = path.node; let body = node.body.body; let id = node.id; let params = node.params; let methods = body.map(method=>{ if(method.kind === "constructor"){ return typs.functionDeclaration(id, method.params, method.body) }else{ // Animal.prototype let left = typs.memberExpression(id,typs.identifier("prototype")); // Animal.prototype.getName left = typs.memberExpression(left,method.key); let right = typs.functionExpression(null,method.params,method.body); return typs.assignmentExpression("=",left,right); } }) path.replaceWithMultiple(methods); } }}
          const result = babel.transform(code, { plugins: [classPlugins]})
          console.log(result.code)



          導(dǎo)入轉(zhuǎn)換


          const babel = require('@babel/core');const types = require('@babel/types');
          const code = `import antd,{Button} from "antd"`;
          const importPlugin = { visitor: { ImportDeclaration(path) { let node = path.node let specifiers = node.specifiers if ( !( specifiers.length == 1 && types.isImportDefaultSpecifier(specifiers[0]) ) ) { specifiers = specifiers.map((specifier) => { let local = types.importDefaultSpecifier(specifier.local); if (types.isImportDefaultSpecifier(specifier)) { return types.importDeclaration([local],types.stringLiteral(node.source.value)) } else { return types.importDeclaration([local],types.stringLiteral(node.source.value+"/lib/"+specifier.local.name)) } }); path.replaceWithMultiple(specifiers) } }, },}
          const result = babel.transform(code, { plugins: [importPlugin],});
          console.log(result.code)

          參考鏈接


          • 看了就懂的AST和Babel工作流程:https://mp.weixin.qq.com/s/m-EJljsARM5dUlu-IXJnKQ
          • 編寫自定義babel轉(zhuǎn)換的分步指南:https://lihautan.com/step-by-step-guide-for-writing-a-babel-transformation/
          • 通過構(gòu)建自己的Babel插件了解AST:https://www.sitepoint.com/understanding-asts-building-babel-plugin/




          點擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動和交流。
          -?END -

          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产日韩在线视频 | 国产精品一级女 | 蜜桃网址一区在线观看 | 蜜臀精品色无码蜜臀AV | 婷婷我也去|