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

本文會(huì)對(duì)babel文檔文檔從一個(gè)推導(dǎo)角度來闡述每個(gè)babel模塊的作用,嘗試?yán)砬迤渲忻}絡(luò),方便快速理解。

本文不是官網(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兩個(gè)工具,前者提供命令行工具函數(shù)對(duì)文件進(jìn)行轉(zhuǎn)譯;后者提供require鉤子:對(duì)node的require函數(shù)改造,對(duì)后續(xù)require函數(shù)在執(zhí)行時(shí)自動(dòng)對(duì)模塊進(jìn)行源碼轉(zhuǎn)譯后在導(dǎo)入。

babel的目標(biāo)是對(duì)代碼進(jìn)行轉(zhuǎn)譯,這個(gè)過程可以拆解為:解析源碼,遍歷ast改造代碼,重新生成代碼這三個(gè)過程。為了提高使用范圍,在v7+版本中,babel將功能拆解出來了多個(gè)工具,主要有:

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

解析源碼

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

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可以簡單的理解為源碼字符串進(jìn)行語法分析后的結(jié)構(gòu)化數(shù)據(jù),方便后續(xù)進(jìn)行檢查或者改造。

ast中的節(jié)點(diǎn)一般還會(huì)包含坐標(biāo)位置,如字符串下標(biāo),行數(shù),列數(shù)等,更多詳細(xì)內(nèi)容請(qǐng)參考官方文檔。

老版本babel中使用的是 acornacorn-jsx,在v7以上時(shí),進(jìn)行了fork改造為@babel/parser。

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

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

當(dāng)前的解析器默認(rèn)只支持最新的es6代碼,如果需要兼容一些新語法(非語法糖之類的新特性,新表達(dá)式和新操作服,如對(duì)象解構(gòu),可選表達(dá)式,類型等),需要擴(kuò)展babel語法插件。

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

遍歷ast改造代碼

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

babel即是處于這樣一個(gè)原因,采用了訪問者模式??梢院唵蔚睦斫鉃椋趯?duì)ast進(jìn)行一個(gè)遍歷時(shí),每次進(jìn)入一個(gè)新的節(jié)點(diǎn)或者退出一個(gè)節(jié)點(diǎn)時(shí),都會(huì)拜訪每一個(gè)插件,咨詢它們是否需要對(duì)當(dāng)前的情況進(jìn)行處理。這鐘架構(gòu)證了性能,也保證了擴(kuò)展性。

另一種插件化架構(gòu),也就是流式架構(gòu),如gulp。也就是插件隊(duì)列依次對(duì)上一個(gè)插件處理后的ast對(duì)象進(jìn)行更深一層次的改造。但這種架構(gòu),需要多次循環(huán)ast, 在實(shí)際使用中,一般一個(gè)生產(chǎn)項(xiàng)目,文件內(nèi)容巨大,文件數(shù)量居多,會(huì)導(dǎo)致性能崩潰。

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

插件化的架構(gòu),也允許使用者進(jìn)行拔插式配置,根據(jù)當(dāng)前使用場景進(jìn)行高度定制。這也就是在配置文件中如babrlrc.js可以配置插件的原因。

為了支持高度動(dòng)態(tài)配置化來適配復(fù)雜的場景,babel會(huì)將每個(gè)插件負(fù)責(zé)的功能劃分足夠小,一般每個(gè)插件只會(huì)負(fù)責(zé)一個(gè)特性。這會(huì)導(dǎo)致使用時(shí),需要去了解每一個(gè)插件的作用,然后在配置文件中配置超長的插件列表,帶來巨大的心智負(fù)擔(dān)和維護(hù)難度。為了解決這個(gè)問題,babel提供了預(yù)設(shè)的機(jī)制。簡單的理解就是一個(gè)babel配置可以繼承另一個(gè)配置,那么我們只需要繼承社區(qū)上或者官方專業(yè)人員配置的預(yù)設(shè)即可,如:

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

為了方便插件中的復(fù)用,babel將遍歷ast的工具也開放出來為一個(gè)單獨(dú)的模塊@babel/traverse。將節(jié)點(diǎn)類型的判斷和創(chuàng)建節(jié)點(diǎn)的工具庫,放在了@babel/types。

另外對(duì)于一些babel中多個(gè)模塊公用的一些工具,都封裝成工具模塊,也就是@babel/helper-*系列模塊,如:

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

由于babel自帶了那么多插件,所以很多helper其實(shí)是插件的輔助工具,如helper-module-imports就是輔助生成一些導(dǎo)入節(jié)點(diǎn)。

在es6+轉(zhuǎn)成es5的過程中,很多語法糖語法(語法上的細(xì)微調(diào)整),如let, const等實(shí)現(xiàn)直接用插件調(diào)整代碼即可解決。但對(duì)于其他的需要大端代碼才能實(shí)現(xiàn)的特性,如Array#includes,生成器,迭代器,async/await, promise等,如果每次都通過代碼展開,那么編譯后的代碼將會(huì)巨大。為了解決這個(gè)問題,會(huì)將includes的實(shí)現(xiàn)放在補(bǔ)丁(polyfill)中,然后直接使用補(bǔ)丁中的實(shí)現(xiàn)。如生成器,迭代器,async/await, promise等都是通過這種機(jī)制支持。

這些的補(bǔ)丁(polyfill)的導(dǎo)入方式也有兩種,一種是全量導(dǎo)入,也就是導(dǎo)入@babel/polyfill模塊。一種是按需導(dǎo)入,需要使用預(yù)設(shè)@babel/preset-env,根據(jù)實(shí)際使用情況,在使用的模塊中按需導(dǎo)入@babel/runtime中的補(bǔ)丁(polyfill)。如:

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

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

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

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

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

生成代碼

使用@babel/generator即可對(duì)一個(gè)ast樹重新生成為代碼。

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

配置

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

更多的配置詳細(xì)使用等請(qǐng)看官網(wǎng)。

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

其他官方工具

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

  • @babel/standalone: 支持瀏覽器上運(yùn)行的babel版本,用于一些在線編輯網(wǎng)站,如JS Bin
  • @babel/code-frame: 代碼窗口,用于輸出類似這種:
  1 | class Foo {
> 2 |   constructor()
    |                ^
  3 | }
  • @babel/template: babel插件開發(fā)工具,支持根據(jù)代碼字符串創(chuàng)建ast節(jié)點(diǎn)。因?yàn)閍st節(jié)點(diǎn)攜帶信息較多,且結(jié)構(gòu)較深,在手動(dòng)創(chuàng)建復(fù)雜的代碼節(jié)點(diǎn)時(shí)十分不便。使用官方提供的這個(gè)工具,可以快速創(chuàng)建一整段代碼節(jié)點(diǎn),并且還支持占位符:
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");
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 了解 Babel 各個(gè)模塊 本文所研究的是 babel 6 版本。babel 6 是 2015年10月30號(hào) 發(fā)布...
    shianqi閱讀 4,879評(píng)論 0 7
  • 引言 babel是一個(gè)非常強(qiáng)大的工具,作用遠(yuǎn)不止我們平時(shí)的ES6 -> ES5語法轉(zhuǎn)換這么單一。在前端進(jìn)階的道路上...
    AlienZHOU閱讀 2,994評(píng)論 0 5
  • 前端工程化之前 前端工程化之前,我們編寫代碼 html js css . 因?yàn)闉g覽器只能讀懂這幾個(gè)代碼因?yàn)閱为?dú)的h...
    川九閱讀 670評(píng)論 0 0
  • Babel是前端很常用的轉(zhuǎn)碼器,更準(zhǔn)確地說是轉(zhuǎn)譯器,是從源碼到源碼的轉(zhuǎn)換編譯器,例如可以將我們按照ES6標(biāo)準(zhǔn)寫的代...
    拉面頭_7c92閱讀 1,343評(píng)論 4 2
  • Babel 是一個(gè)編譯器,和其他編譯器一樣,編譯過程分為三個(gè)階段,分別是解析(parsing)、轉(zhuǎn)換(transf...
    暖A暖閱讀 214評(píng)論 0 0

友情鏈接更多精彩內(nèi)容