<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>

          JavaScrpit AST 實(shí)戰(zhàn)

          共 4658字,需瀏覽 10分鐘

           ·

          2021-03-25 15:10

          關(guān)注 前端瓶子君,回復(fù)“交流

          加入我們一起學(xué)習(xí),天天進(jìn)步

          作者:cd2001cjm(本文來(lái)自作者投稿)

          https://www.jianshu.com/p/8bbc8f43a2ae

          前言


          每個(gè)編程語(yǔ)言都有自己的AST,了解AST并能進(jìn)行一些開發(fā),會(huì)給我們的項(xiàng)目開發(fā)提供很大的便利。下面就帶大家一探究竟


          通過本文能了解到什么


          1. JS AST結(jié)構(gòu)和屬性

          2. babel插件開發(fā)


          JS AST簡(jiǎn)介


          AST也就是抽象語(yǔ)法樹。簡(jiǎn)單來(lái)說(shuō)就是把程序用樹狀形式展現(xiàn)。

          每種語(yǔ)言(HTML,CSS,JS等)都有自己的AST,而且還有多種AST解析器。


          回歸JS本身,常見的AST解析器有:


          • acorn

          • @babel/parser

          • Typescript

          • Uglify-js

          • 等等


          不同解析器解析出來(lái)的AST有些許差異,但本質(zhì)上是一樣的。

          本文將基于@babel/parser來(lái)進(jìn)行示例和講解


          下面來(lái)看一句常見的代碼


          import ajax from 'axios'


          轉(zhuǎn)換后的AST結(jié)構(gòu)如下:


          {        "type": "ImportDeclaration",        "start": 0,        "end": 24,        "loc": {          "start": {            "line": 1,            "column": 0          },          "end": {            "line": 1,            "column": 24          }        },        "specifiers": [          {            "type": "ImportDefaultSpecifier",            "start": 7,            "end": 11,            "loc": {              "start": {                "line": 1,                "column": 7              },              "end": {                "line": 1,                "column": 11              }            },            "local": {              "type": "Identifier",              "start": 7,              "end": 11,              "loc": {                "start": {                  "line": 1,                  "column": 7                },                "end": {                  "line": 1,                  "column": 11                },                "identifierName": "ajax"              },              "name": "ajax"            }          }        ],        "importKind": "value",        "source": {          "type": "StringLiteral",          "start": 17,          "end": 24,          "loc": {            "start": {              "line": 1,              "column": 17            },            "end": {              "line": 1,              "column": 24            }          },          "extra": {            "rawValue": "axios",            "raw": "'axios'"          },          "value": "axios"        }      }


          內(nèi)容是不是比想象的多?莫慌,我們一點(diǎn)一點(diǎn)看。


          來(lái)一張簡(jiǎn)略圖:



          ImportDeclaration


          語(yǔ)句的類型,表明是一個(gè)import的聲明。


          常見的有:


          - VariableDeclaration:var x = 'init'

          - FunctionDeclaration:function func(){}

          - ExportNamedDeclaration:export function exp(){}

          - IfStatement:if(1>0){}

          - WhileStatement:while(true){}

          - ForStatement:for(;;){}

          - 不一一列舉


          既然是一個(gè)引入表達(dá)式,自然分左右兩部分,左邊的是specifiers,右邊的是source


          specifiers


          specifiers節(jié)點(diǎn)會(huì)有一個(gè)列表來(lái)保存specifier

          如果左邊只聲明了一個(gè)變量,那么會(huì)給一個(gè)ImportDefaultSpecifier

          如果左邊是多個(gè)聲明,就會(huì)是一個(gè)ImportSpecifier列表

          什么叫左邊有多個(gè)聲明?看下面的示例



          import {a,b,c} from 'x'


          變量的聲明要保持唯一性

          而Identifier就是鼓搗這個(gè)事情的


          source


          source包含一個(gè)字符串節(jié)點(diǎn)StringLiteral,對(duì)應(yīng)了引用資源所在位置。示例中就是axios


          AST是如何轉(zhuǎn)換出來(lái)的呢?


          以babel為例子:


          const parser = require('@babel/parser')let codeString = `import ajax from 'axios'`;

          let file = parser.parse(codeString,{ sourceType: "module"})console.dir(file.program.body)


          在node里執(zhí)行一下,就能打印出AST


          通過這個(gè)小示例,大家應(yīng)該對(duì)AST有個(gè)初步的了解,下面我們談?wù)劻私馑惺裁匆饬x


          應(yīng)用場(chǎng)景以及實(shí)戰(zhàn)


          實(shí)際上,我們?cè)陧?xiàng)目中,AST技術(shù)隨處可見


          • Babel對(duì)es6語(yǔ)法的轉(zhuǎn)換

          • Webpack對(duì)依賴的收集

          • Uglify-js對(duì)代碼的壓縮

          • 組件庫(kù)的按需加載babel-plugin

          • 等等


          為了更好的理解AST,我們定義一個(gè)場(chǎng)景,然后實(shí)戰(zhàn)一下。

          場(chǎng)景:把import轉(zhuǎn)換成require,類似于babel的轉(zhuǎn)換

          目標(biāo):通過AST轉(zhuǎn)換,把語(yǔ)句



          import ajax from 'axios'


          轉(zhuǎn)為



          var ajax = require('axios')


          要達(dá)到這個(gè)效果,首先我們要寫一個(gè)babel-plugin。先上代碼


          babelPlugin.js代碼如下:


          const t = require('@babel/types');

          module.exports = function babelPlugin(babel) {

          function RequireTranslator(path){

          var node = path.node var specifiers = node.specifiers

          //獲取變量名稱 var varName = specifiers[0].local.name; //獲取資源地址 var source = t.StringLiteral(path.node.source.value) var local = t.identifier(varName) var callee = t.identifier('require') var varExpression = t.callExpression(callee,[source]) var declarator = t.variableDeclarator(local, varExpression) //創(chuàng)建新節(jié)點(diǎn) var newNode = t.variableDeclaration("var", [declarator]) //節(jié)點(diǎn)替換 path.replaceWith(newNode)

          }

          return { visitor: { ImportDeclaration(path) { RequireTranslator.call(this,path) } } };};


          測(cè)試代碼:


          const babel = require('@babel/core');const babelPlugin = require('./babelPlugin')

          let codeString = `import ajax from 'axios'`;const plugins = [babelPlugin]const {code} = babel.transform(codeString,{plugins:plugins});console.dir(code)


          輸出結(jié)果:


          'var ajax = require("axios");'


          目標(biāo)達(dá)成!


          babel-plugin


          在babel的官網(wǎng)有開發(fā)文檔,這里只是簡(jiǎn)單的描述一下注意要點(diǎn):


          • 插件要求返回一個(gè)visitor對(duì)象。

          • 可以攔截所有的節(jié)點(diǎn),函數(shù)名稱就是節(jié)點(diǎn)類型,入?yún)⑹莗ath,可以通過path.node來(lái)獲取當(dāng)前節(jié)點(diǎn)

          • @babel/types提供了大量節(jié)點(diǎn)操作的API,同樣可以在官網(wǎng)看的詳細(xì)的說(shuō)明


          transform


          這里的代碼大家是不是看著很熟悉。沒錯(cuò),就是.babelrc里的配置。我們開發(fā)的插件,配置到.babelrc的plugins里,就可以全局運(yùn)行了。


          寫在最后


          JS的AST,給我們提供了實(shí)現(xiàn)各種可能得機(jī)會(huì)。我們可以自定義一個(gè)語(yǔ)法,可以將組件的按需引入過程簡(jiǎn)化等等。同時(shí)不僅僅是JS,CSS,HTML,SQL都可以在ast語(yǔ)法級(jí)別去進(jìn)行一些有趣的操作。該篇文章只是帶大家簡(jiǎn)單入門。寫在最后:前端不僅僅是UI,可玩的東西還有很多

          最后

          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復(fù)「算法」,加入前端算法源碼編程群,每日一刷(工作日),每題瓶子君都會(huì)很認(rèn)真的解答喲!
          回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對(duì)你有幫助,在看」是最大的支持
          》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持
          瀏覽 65
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  在线免费观看国产精品 | 伊人影院污 | 免费小视频 | 五月丁香好婷婷网站入口 | 亚洲阿v天堂 |