babel對(duì)于一個(gè)前端應(yīng)該并不陌生,在我們隨心所欲的使用ESnext的語(yǔ)法的時(shí)候,babel為我們默默的做著轉(zhuǎn)換工作。那我們可能會(huì)好奇babel到底是怎么做到的呢,接下來(lái)我們就從手寫(xiě)一個(gè)babel插件入手來(lái)一起學(xué)習(xí)學(xué)習(xí)。
首先我們要研究一個(gè)東西得弄清楚它到底是干什么,引用babel官網(wǎng)的描述:

我們首先思考一下babel要將最新的語(yǔ)法進(jìn)行轉(zhuǎn)換,肯定是有一套描述語(yǔ)法的數(shù)據(jù)結(jié)構(gòu),是的它就是AST(抽象語(yǔ)法樹(shù)),看到這玩意是不是有點(diǎn)有頭緒了。有了AST,數(shù)據(jù)結(jié)構(gòu)我們就有了,剩下的就剩匹配轉(zhuǎn)換了。
開(kāi)始寫(xiě)代碼之前我們先來(lái)介紹一下babel提供的工具:
1.https://astexplorer.net/ (在線js代碼轉(zhuǎn)換為AST)
2.babylon(js轉(zhuǎn)換為AST的庫(kù))
3.babel-traverse(AST遍歷庫(kù))
4.babel-types(包含了構(gòu)造、驗(yàn)證以及變換 AST 節(jié)點(diǎn)的方法)
工欲善其事必先利其器,我們的工具都有了,接下來(lái)我們創(chuàng)建一個(gè)項(xiàng)目,項(xiàng)目的目錄結(jié)構(gòu)如下:

index.js就是我們的插件入口,_test_為單元測(cè)試,test.js是我們需要轉(zhuǎn)換的文件。接下來(lái)我們一起來(lái)看一看index.js文件

我們這個(gè)插件主要功能是實(shí)現(xiàn)按照配置去除代碼中的console,debugger,以及自定義的一個(gè)DEBUG的一個(gè)調(diào)試關(guān)鍵字(用于在指定環(huán)境下需要執(zhí)行的代碼)。
我們可以看到這邊導(dǎo)出了一個(gè)function,function返回的是一個(gè)對(duì)象,這個(gè)對(duì)象內(nèi)部有一個(gè)visitor屬性,這visitor也是我們最核心的一個(gè)屬性(AST的訪問(wèn)者),這個(gè)訪問(wèn)者是必須要提供的,也可以理解為固定寫(xiě)法。
我們看到導(dǎo)出的function有一個(gè)參數(shù)t,這個(gè)t是干什么的呢?這其實(shí)就是我們上面提到的babel-types。它可以讓我們更方便的去匹配(if, else, for)這些關(guān)鍵字,從而定位到需要執(zhí)行我們自己邏輯的代碼塊。
那visitor內(nèi)部的這些東西又是什么呢,這個(gè)我們就得打開(kāi)我們上面提到的AST轉(zhuǎn)換網(wǎng)站,將我們需要處理的代碼貼進(jìn)去觀察

以debugger為例,我們看到debugger對(duì)應(yīng)的AST里面的key為DebuggerStatement,然后我們?cè)趘isitor里面也看到了DebuggerStatement,是不是有點(diǎn)眉目了,沒(méi)錯(cuò),這個(gè)key其實(shí)也是做類(lèi)型匹配的。比如debugger,我們就可以在DebuggerStatement內(nèi)去處理自己的邏輯,DebuggerStatement的value是一個(gè)function,提供有兩個(gè)參數(shù)(path, state)path就是當(dāng)前配備到的節(jié)點(diǎn)上下文,state我們經(jīng)常的用法是獲取babel插件配置的參數(shù)。到了這一步,我們回憶我們的需求:根據(jù)配置是否去除(debugger, console, 自定義關(guān)鍵字),現(xiàn)在配置能拿到了,接下來(lái)就是如何去除,這就得在path上面研究了,查詢文檔我們可以看到path提供了replaceWith,remove方法(nice),那我們的思路就很清晰了,匹配到關(guān)鍵字,刪除就OK了。
接下來(lái)修改我們的package.json文件:

npm run babel,可以看到這個(gè)插件完美的實(shí)現(xiàn)了我們的需求.。對(duì)于關(guān)鍵字和console的處理基本大同小異,需要注意的是自定義關(guān)鍵字需要處理eslint校驗(yàn)。最后附上完整代碼地址:https://github.com/wangKXX/babel-plugin