Babel內(nèi)部模塊概述

本文會對babel文檔文檔從一個推導角度來闡述每個babel模塊的作用,嘗試理清其中脈絡咳榜,方便快速理解。

本文不是官網(wǎng)的copyer或者中文翻譯

<a name="xVQ3u"></a>

核心

babel的核心功能在@babel/core包中爽锥,核心api為transform系列函數(shù):

babel.transform(code, options, function(err, result) {
  result; // => { code, map, ast }
});

該函數(shù)可以將es6+代碼轉(zhuǎn)譯成es5代碼涌韩,所以被廣泛集成在其他工具里面,完成代碼的轉(zhuǎn)譯工作氯夷,如babel-loader內(nèi)部就是調(diào)該api臣樱。

在babel中,還提供了@babel/cli@babel/register兩個工具腮考,前者提供命令行工具函數(shù)對文件進行轉(zhuǎn)譯雇毫;后者提供require鉤子:對node的require函數(shù)改造,對后續(xù)require函數(shù)在執(zhí)行時自動對模塊進行源碼轉(zhuǎn)譯后在導入踩蔚。

babel的目標是對代碼進行轉(zhuǎn)譯棚放,這個過程可以拆解為:解析源碼,遍歷ast改造代碼馅闽,重新生成代碼這三個過程飘蚯。為了提高使用范圍,在v7+版本中福也,babel將功能拆解出來了多個工具局骤,主要有:

  • 解析源碼: @babel/parser;
  • 遍歷ast改造代碼: @babel/traverse@babel/plugin-*;
  • 重新生成代碼:@babel/generator;
    <a name="Wh9AE"></a>

解析源碼

將源碼解析,生成ast(抽象語法樹)暴凑,會將:

function square(n) {
  return n * n;
}

解析成:

{
  type: "FunctionDeclaration",
  id: {
    type: "Identifier",
    name: "square"
  },
  params: [{
    type: "Identifier",
    name: "n"
  }],
  body: {
    type: "BlockStatement",
    body: [{
      type: "ReturnStatement",
      argument: {
        type: "BinaryExpression",
        operator: "*",
        left: {
          type: "Identifier",
          name: "n"
        },
        right: {
          type: "Identifier",
          name: "n"
        }
      }
    }]
  }
}

ast可以簡單的理解為源碼字符串進行語法分析后的結構化數(shù)據(jù)峦甩,方便后續(xù)進行檢查或者改造。

ast中的節(jié)點一般還會包含坐標位置现喳,如字符串下標凯傲,行數(shù),列數(shù)等拿穴,更多詳細內(nèi)容請參考官方文檔泣洞。

老版本babel中使用的是 acornacorn-jsx忧风,在v7以上時默色,進行了fork改造為@babel/parser

另外@babel/core也集成了@babel/parser功能,可以直接從@babel/core中導出api直接使用:

babel.parse(code: string, options?: Object, callback: Function)

當前的解析器默認只支持最新的es6代碼腿宰,如果需要兼容一些新語法(非語法糖之類的新特性呕诉,新表達式和新操作服,如對象解構吃度,可選表達式甩挫,類型等),需要擴展babel語法插件椿每。

很多工具其實只需要解析代碼即可伊者,如代碼檢驗,如語法高亮间护,源碼中數(shù)據(jù)收集亦渗。
<a name="rzqMT"></a>

遍歷ast改造代碼

講過解析器已經(jīng)將源碼解析成更好處理的結構化數(shù)據(jù)ast,如果需求是對代碼進行調(diào)整汁尺,只需對ast數(shù)據(jù)進行調(diào)整法精,然后使用生成器生成新的代碼即可。但整個babel需要解決的是將所有最新的es6+特性轉(zhuǎn)譯成向后兼容的瀏覽器可執(zhí)行代碼(es5),需要處理的情況眾多痴突,如果直接對ast進行改造搂蜓,那么代碼將非常臃腫。且es規(guī)范還在不停的迭代中辽装,臃腫的代碼的對后續(xù)維護迭代也帶來巨大的挑戰(zhàn)帮碰。針對這種困境,必須需要進行架構上的調(diào)整拾积,使用插件化架構收毫。

babel即是處于這樣一個原因,采用了訪問者模式殷勘〈嗽伲可以簡單的理解為,在對ast進行一個遍歷時玲销,每次進入一個新的節(jié)點或者退出一個節(jié)點時输拇,都會拜訪每一個插件,咨詢它們是否需要對當前的情況進行處理贤斜。這鐘架構證了性能策吠,也保證了擴展性。

另一種插件化架構瘩绒,也就是流式架構猴抹,如gulp。也就是插件隊列依次對上一個插件處理后的ast對象進行更深一層次的改造锁荔。但這種架構蟀给,需要多次循環(huán)ast, 在實際使用中,一般一個生產(chǎn)項目,文件內(nèi)容巨大跋理,文件數(shù)量居多择克,會導致性能崩潰。

所以babel核心框架中前普,只包含了訪問節(jié)點和調(diào)用插件的邏輯肚邢。實際對ast的改造,全部轉(zhuǎn)交給了插件拭卿。這也是為什么babel自帶了那么多@babel/plugin-*插件骡湖。同樣社區(qū)也擁有非常多的插件,從能能夠支持flow, typescipt這些新語法峻厚。

插件化的架構勺鸦,也允許使用者進行拔插式配置,根據(jù)當前使用場景進行高度定制目木。這也就是在配置文件中如babrlrc.js可以配置插件的原因换途。

為了支持高度動態(tài)配置化來適配復雜的場景,babel會將每個插件負責的功能劃分足夠小刽射,一般每個插件只會負責一個特性军拟。這會導致使用時,需要去了解每一個插件的作用誓禁,然后在配置文件中配置超長的插件列表懈息,帶來巨大的心智負擔和維護難度。為了解決這個問題摹恰,babel提供了預設的機制辫继。簡單的理解就是一個babel配置可以繼承另一個配置,那么我們只需要繼承社區(qū)上或者官方專業(yè)人員配置的預設即可俗慈,如:

  • @babel/preset-env
  • @babel/preset-react
  • @babel/preset-typescript
  • @babel/preset-flow

為了方便插件中的復用姑宽,babel將遍歷ast的工具也開放出來為一個單獨的模塊@babel/traverse。將節(jié)點類型的判斷和創(chuàng)建節(jié)點的工具庫闺阱,放在了@babel/types炮车。

另外對于一些babel中多個模塊公用的一些工具,都封裝成工具模塊酣溃,也就是@babel/helper-*系列模塊瘦穆,如:

  • @babel/helper-compilation-targets
  • @babel/helper-module-imports

由于babel自帶了那么多插件,所以很多helper其實是插件的輔助工具赊豌,如helper-module-imports就是輔助生成一些導入節(jié)點扛或。

在es6+轉(zhuǎn)成es5的過程中,很多語法糖語法(語法上的細微調(diào)整)碘饼,如let, const等實現(xiàn)直接用插件調(diào)整代碼即可解決熙兔。但對于其他的需要大端代碼才能實現(xiàn)的特性悲伶,如Array#includes,生成器黔姜,迭代器拢切,async/await, promise等蒂萎,如果每次都通過代碼展開秆吵,那么編譯后的代碼將會巨大。為了解決這個問題五慈,會將includes的實現(xiàn)放在補丁(polyfill)中纳寂,然后直接使用補丁中的實現(xiàn)。如生成器泻拦,迭代器毙芜,async/await, promise等都是通過這種機制支持。

這些的補丁(polyfill)的導入方式也有兩種争拐,一種是全量導入腋粥,也就是導入@babel/polyfill模塊。一種是按需導入架曹,需要使用預設@babel/preset-env隘冲,根據(jù)實際使用情況,在使用的模塊中按需導入@babel/runtime中的補丁(polyfill)绑雄。如:

var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");

var Circle = function Circle() {
  _classCallCheck(this, Circle);
};

@babel/polyfill@babel/runtime的底層實現(xiàn)都是core-js展辞。

實際情景下,還是存在插件無法解決的情況:一個無法用老代碼補丁實現(xiàn)万牺,也無法使用語法糖替換代碼的特性罗珍,如Proxy對象,這種特性一般需要js引擎從底層提供脚粟。在使用這些特性時覆旱,需要注意瀏覽器兼容性。

<a name="uaLlx"></a>

生成代碼

使用@babel/generator即可對一個ast樹重新生成為代碼核无。

<a name="S8g5j"></a>

配置

我們通常見到的babel配置就是就是用于指導babel行為的配置文件通殃,可以簡單的理解為@babel/coretransform函數(shù)的選項支持使用配置文件配置。

更多的配置詳細使用等請看官網(wǎng)厕宗。

<a name="sRLPS"></a>

其他官方工具

babel還提供了一些其他工具画舌,用于擴展babel生態(tài)鏈:

  • @babel/standalone: 支持瀏覽器上運行的babel版本,用于一些在線編輯網(wǎng)站已慢,如JS Bin
  • @babel/code-frame: 代碼窗口曲聂,用于輸出類似這種:
  1 | class Foo {
> 2 |   constructor()
    |                ^
  3 | }
  • @babel/template: babel插件開發(fā)工具,支持根據(jù)代碼字符串創(chuàng)建ast節(jié)點佑惠。因為ast節(jié)點攜帶信息較多朋腋,且結構較深齐疙,在手動創(chuàng)建復雜的代碼節(jié)點時十分不便。使用官方提供的這個工具旭咽,可以快速創(chuàng)建一整段代碼節(jié)點贞奋,并且還支持占位符:
const buildRequire = template(`
  var IMPORT_NAME = require(SOURCE);
`);

const ast = buildRequire({
  IMPORT_NAME: t.identifier("myModule"),
  SOURCE: t.stringLiteral("my-module"),
});

//  ||   ||   ||
// \\// \\// \\//

const myModule = require("my-module");
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市穷绵,隨后出現(xiàn)的幾起案子轿塔,更是在濱河造成了極大的恐慌,老刑警劉巖仲墨,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勾缭,死亡現(xiàn)場離奇詭異,居然都是意外死亡目养,警方通過查閱死者的電腦和手機俩由,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來癌蚁,“玉大人幻梯,你說我怎么就攤上這事∨停” “怎么了碘梢?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長洽洁。 經(jīng)常有香客問我痘系,道長,這世上最難降的妖魔是什么饿自? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任汰翠,我火速辦了婚禮,結果婚禮上昭雌,老公的妹妹穿的比我還像新娘复唤。我一直安慰自己,他們只是感情好烛卧,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布佛纫。 她就那樣靜靜地躺著,像睡著了一般总放。 火紅的嫁衣襯著肌膚如雪呈宇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天局雄,我揣著相機與錄音甥啄,去河邊找鬼。 笑死炬搭,一個胖子當著我的面吹牛蜈漓,可吹牛的內(nèi)容都是我干的穆桂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼融虽,長吁一口氣:“原來是場噩夢啊……” “哼享完!你這毒婦竟也來了?” 一聲冷哼從身側響起有额,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤般又,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谆吴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倒源,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡苛预,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年句狼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片热某。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡腻菇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昔馋,到底是詐尸還是另有隱情筹吐,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布秘遏,位于F島的核電站丘薛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏邦危。R本人自食惡果不足惜洋侨,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望倦蚪。 院中可真熱鬧希坚,春花似錦、人聲如沸陵且。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慕购。三九已至聊疲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沪悲,已是汗流浹背获洲。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留可训,地道東北人昌妹。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓捶枢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親飞崖。 傳聞我的和親對象是個殘疾皇子烂叔,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 了解 Babel 各個模塊 本文所研究的是 babel 6 版本。babel 6 是 2015年10月30號 發(fā)布...
    shianqi閱讀 4,568評論 0 7
  • 引言 babel是一個非常強大的工具固歪,作用遠不止我們平時的ES6 -> ES5語法轉(zhuǎn)換這么單一蒜鸡。在前端進階的道路上...
    AlienZHOU閱讀 2,864評論 0 5
  • 前端工程化之前 前端工程化之前,我們編寫代碼 html js css . 因為瀏覽器只能讀懂這幾個代碼因為單獨的h...
    川九閱讀 535評論 0 0
  • Babel是前端很常用的轉(zhuǎn)碼器,更準確地說是轉(zhuǎn)譯器牢裳,是從源碼到源碼的轉(zhuǎn)換編譯器逢防,例如可以將我們按照ES6標準寫的代...
    拉面頭_7c92閱讀 1,201評論 4 2
  • Babel 是一個編譯器,和其他編譯器一樣蒲讯,編譯過程分為三個階段忘朝,分別是解析(parsing)、轉(zhuǎn)換(transf...
    暖A暖閱讀 119評論 0 0