一篇文章讀懂babel

本文將從下面幾個問題來回答對babel對理解:

1. 什么是bable;
2. bable的工作原理是什么媒区;
3. babel應該怎么使用、任何配置舶得;

一、什么是babel:

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

我們已經(jīng)能夠熟練地使用 es2015+ 的語法咱娶。但是對于瀏覽器來說缭贡,可能和它們還不夠熟悉,我們得讓瀏覽器理解它們破衔,這就需要 Babel.

Babel 是一個通用的多功能 JavaScript 編譯器清女,但與一般編譯器不同的是它只是把同種語言的高版本規(guī)則轉(zhuǎn)換為低版本規(guī)則,而不是輸出另一種低級機器可識別的代碼晰筛,并且在依賴不同的拓展插件下可用于不同形式的靜態(tài)分析嫡丙。(靜態(tài)分析:指在不需要執(zhí)行代碼的前提下對代碼進行分析以及相應處理的一個過程忠售,主要應用于語法檢查、編譯迄沫、代碼高亮、代碼轉(zhuǎn)換卦方、優(yōu)化羊瘩、壓縮等等)

二、bable的工作原理是什么:

可以理解babel本質(zhì)也是一個編譯器盼砍,只是這個編譯器輸出對不是二進制機器碼而是在瀏覽器能運行對js語法. 所以更準確點說他是一個轉(zhuǎn)譯器尘吗,它工作分為3個步驟:

1. 解析 Parse
2. 轉(zhuǎn)換 Transform
3. Generate
babel.jpg
  • 解析 Parse

將代碼解析生成抽象語法樹( 即AST ),也就是計算機理解我們代碼的方式(擴展:一般來說每個 js 引擎都有自己的 AST浇坐,比如熟知的 v8睬捶,chrome 瀏覽器會把 js 源碼轉(zhuǎn)換為抽象語法樹,再進一步轉(zhuǎn)換為字節(jié)碼或機器代碼)近刘,而 babel 則是通過 babylon 實現(xiàn)的 擒贸。簡單來說就是一個對于 JS 代碼的一個編譯過程,進行了詞法分析與語法分析的過程觉渴。

談到AST就必須理解js引擎是如何理解我們代碼的介劫,簡答來說是兩個步驟:1、分詞; 2案淋、語意分析座韵。

1、分詞:

將整個代碼字符串分割成語法單元數(shù)組(token)

JS 代碼中的語法單元主要指如標識符(if/else踢京、return誉碴、function)、運算符瓣距、括號黔帕、數(shù)字、字符串旨涝、空格等等能被解析的最小單元蹬屹。比如下面的代碼生成的語法單元數(shù)組如下:

分詞小工具

var answer = 6 * 7;
=> 
//分詞token
[
    {
        "type": "Keyword",
        "value": "var"
    },
    {
        "type": "Identifier",
        "value": "answer"
    },
    {
        "type": "Punctuator",
        "value": "="
    },
    {
        "type": "Numeric",
        "value": "6"
    },
    {
        "type": "Punctuator",
        "value": "*"
    },
    {
        "type": "Numeric",
        "value": "7"
    },
    {
        "type": "Punctuator",
        "value": ";"
    }
]

2、語意分析:

語義分析則是將得到的詞匯進行一個立體的組合白华,確定詞語之間的關(guān)系慨默。考慮到編程語言的各種從屬關(guān)系的復雜性弧腥,語義分析的過程又是在遍歷得到的語法單元組厦取,相對而言就會變得更復雜。

簡單來說語義分析既是對語句和表達式識別管搪,這是個遞歸過程虾攻,在解析中铡买,babel 會在解析每個語句和表達式的過程中設置一個暫存器,用來暫存當前讀取到的語法單元霎箍,如果解析失敗奇钞,就會返回之前的暫存點,再按照另一種方式進行解析漂坏,如果解析成功景埃,則將暫存點銷毀,不斷重復以上操作顶别,直到最后生成對應的語法樹谷徙。

{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "answer"
                    },
                    "init": {
                        "type": "BinaryExpression",
                        "operator": "*",
                        "left": {
                            "type": "Literal",
                            "value": 6,
                            "raw": "6"
                        },
                        "right": {
                            "type": "Literal",
                            "value": 7,
                            "raw": "7"
                        }
                    }
                }
            ],
            "kind": "var"
        }
    ],
    "sourceType": "script"
}
  • 轉(zhuǎn)換 Transform

對于 AST 進行變換一系列的操作,babel 接受得到 AST 并通過 babel-traverse 對其進行遍歷驯绎,在此過程中進行添加完慧、更新及移除等操作

  • 生成 Generate

將變換后的 AST 再轉(zhuǎn)換為JS代碼, 使用到的模塊是 babel-generator。

而 babel-core 模塊則是將三者結(jié)合使得對外提供的API做了一個簡化剩失。

此外需要注意的是屈尼,babel 只是轉(zhuǎn)譯新標準引入的語法,比如ES6箭頭函數(shù):而新標準引入的新的原生對象赴叹,部分原生對象新增的原型方法鸿染,新增的 API 等(Proxy、Set 等), 這些事不會轉(zhuǎn)譯的乞巧,需要引入對應的 polyfill 來解決涨椒。

而我們編寫的 babel 插件則主要專注于第二步轉(zhuǎn)換過程的工作,專注于對于代碼的轉(zhuǎn)化規(guī)則的拓展绽媒,解析與生成的偏底層相關(guān)操作則有對應的模塊支持蚕冬,在此我們理解它主要做了什么即可。

經(jīng)過babel轉(zhuǎn)換后高版本js語法就變成了ECMAScript 2015+ 的代碼是辕,支持在舊的瀏覽器上運行囤热,如:

// es2015 的 const 和 arrow function
const add = (a, b) => a + b;
let a =1;
let b ={a};

// Babel 轉(zhuǎn)譯后
var add = function add(a, b) {
  return a + b;
};
var a = 1;
var b = { a: a };

三、babel該如何使用及配置:

目前JS沒有統(tǒng)一的構(gòu)建構(gòu)建获三、平臺旁蔼,所以babel支持對各種工具對基gulp、webpack疙教、node

開始使用babel之前我們首先理解幾個babel文件:

babel-cli 是一種在命令行下使用Babel編譯工具
babel-register 在node下運行babel的小工具
babel-polyfill Babel一起使用的polyfill是babel-polyfill
babele-runtime 轉(zhuǎn)換api工具
babel-core Babel 的核心代碼
babel-loader 解析 js 的 loader
babel-preset-react 用于解析 jsx 語法
babel-preset-env 取代之前的 babel-preset-es2015 babel-preset-es2016 等包棺聊,解析 ES6 等語法
babel-preset-stage-0 同類的還有 stage-1、stage-2贞谓、stage-3限佩,stage-0 包含es7的解析內(nèi)容,且同時包含 stage-1/2/3 的所有功能

* 以上是babel6,如果需要使用babel7的話需要增加@前綴,@babel-core
  • babel-cli

從名字就可以理解祟同,這個是一個命令行工具作喘,運行在node環(huán)境,能將es6或者es7語法的js文件轉(zhuǎn)換為es5文件晕城。

$ npm install --global babel-cli
$ babel example.js --out-file compiled.js
# 或
$ babel example.js -o compiled.js

  • babel-register

babel-cli會把一個文件轉(zhuǎn)換成一個es5文件執(zhí)行泞坦,但是如果你想直接執(zhí)行這個文件:

$ node index.js

直接來運行它是不會使用 Babel 來編譯的,這個時候就需要使用babel-register砖顷,首先在項目中創(chuàng)建 register.js 文件并添加如下代碼:

require("babel-register");
require("./index.js");

這樣做可以把 Babel 注冊到 Node 的模塊系統(tǒng)中并開始編譯其中 require 的所有文件暇矫。

現(xiàn)在我們可以使用 register.js 來代替 node index.js 來運行了。

$ node register.js
  • babel-polyfill與babele-runtime

Babel默認只轉(zhuǎn)換新的JavaScript語法择吊,而不轉(zhuǎn)換新的API。 例如槽奕,Iterator几睛、Generator、Set粤攒、Maps所森、Proxy、Reflect夯接、Symbol焕济、Promise 等全局對象,以及一些定義在全局對象上的方法(比如 Object.assign)都不會轉(zhuǎn)譯盔几。 如果想使用這些新的對象和方法晴弃,則需要為當前環(huán)境提供一個polyfill

目前最常用的配合Babel一起使用的polyfill是babel-polyfill,它會”加載整個polyfill庫”逊拍,針對編譯的代碼中新的API進行處理上鞠,并且在代碼中插入一些幫助函數(shù)。

babel-polyfill解決了Babel不轉(zhuǎn)換新API的問題芯丧,但是直接在代碼中插入幫助函數(shù)芍阎,會導致污染了全局環(huán)境,并且不同的代碼文件中包含重復的代碼缨恒,導致編譯后的代碼體積變大谴咸。 (比如:上述的幫助函數(shù)_defineProperty有可能在很多的代碼模塊文件中都會被插入)

Babel為了解決這個問題,提供了單獨的包babel-runtime用以提供編譯模塊的工具函數(shù)骗露, 啟用插件babel-plugin-transform-runtime后岭佳,Babel就會使用babel-runtime下的工具函數(shù),上述的代碼就會變成這樣

  • babel-core

以上都是基于cli或者node環(huán)境時使用babel椒袍,如果你想通過代碼的方式實現(xiàn)babel驼唱,這個時候就需要使用babel-core

$ npm install babel-core
var babel = require("babel-core");

字符串形式的 JavaScript 代碼可以直接使用 babel.transform 來編譯。

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

配置 Babel

目前為止通過運行 Babel 自己我們并沒能“翻譯”代碼驹暑,而僅僅是把代碼從一處拷貝到了另一處玫恳。

這是因為我們還沒告訴 Babel 要做什么辨赐。

你可以通過安裝插件(plugins)或預設(presets,也就是一組插件來指示 Babel 去做什么事情京办。

  • .babelrc

在我們告訴 Babel 該做什么之前掀序,我們需要創(chuàng)建一個配置文件。你需要做的就是在項目的根路徑下創(chuàng)建 .babelrc 文件惭婿。然后輸入以下內(nèi)容作為開始:

{
  "presets": [],
  "plugins": []
}

這個文件就是用來讓 Babel 做你要它做的事情的配置文件不恭。

  • plugins

上面說過babel本身是不會去翻譯代碼,需要插件告訴babel需要做什么财饥,比如你需要翻譯箭頭函數(shù)就需要transform-es2015-arrow-functions插件换吧。

  • presets(預設)

不想自己動手組合插件?沒問題钥星!preset 可以作為 Babel 插件的組合沾瓦,甚至可以作為可以共享的 options 配置。

官方針對常用環(huán)境編寫了一些 preset:

babel/preset-env
babel/preset-flow
babel/preset-react 翻譯jsx文件
babel/preset-typescript
babel-preset-react-app creat-react-app
babel-preset-es2015 es6=>es5
babel-preset-stage-x 提案階段語法
  • presets與plugins差別

舉個例子谦炒, babel-preset-es2015 包含了 transform-es2015-arrow-functions(編譯箭頭函數(shù))贯莺、transform-es2015-block-scoping(編譯 const、let)宁改、transform-es2015-classes(編譯class)等幾十個插件

如果不使用 preset缕探,如果想要用完整的 es6 語法,使用者可能需要自己配置幾十個 plugins还蹲。

  • babel-preset-env

單獨講一下babel-preset-env 這個預設插件爹耗,
為了讓開發(fā)者能夠盡早用上新的JS特性,babel團隊開發(fā)了babel-preset-latest谜喊。這個preset比較特殊鲸沮,它是多個preset的集合(es2015+),并且隨著ECMA規(guī)范的更新更增加它的內(nèi)容锅论。

隨著時間的推移讼溺,babel-preset-latest 包含的插件越來越多,這帶來了如下問題:

1最易、加載的插件越來越多怒坯,編譯速度會越來越慢;

2藻懒、隨著用戶瀏覽器的升級剔猿,ECMA規(guī)范的支持逐步完善,編譯至低版本規(guī)范的必要性在減少(比如ES6 -> ES5)嬉荆,多余的轉(zhuǎn)換不單降低執(zhí)行效率归敬,還浪費帶寬。

因為上述問題的存在,babel官方推出了babel-preset-env插件汪茧。它可以根據(jù)開發(fā)者的配置椅亚,按需加載插件。

需要支持的平臺:比如node舱污、瀏覽器等呀舔。
需要支持的平臺的版本:比如支持node@6.1等。

參考文獻

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扩灯,一起剝皮案震驚了整個濱河市媚赖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌珠插,老刑警劉巖惧磺,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捻撑,居然都是意外死亡豺妓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門布讹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人训堆,你說我怎么就攤上這事描验。” “怎么了坑鱼?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵膘流,是天一觀的道長。 經(jīng)常有香客問我鲁沥,道長呼股,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任画恰,我火速辦了婚禮彭谁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘允扇。我一直安慰自己缠局,他們只是感情好,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布考润。 她就那樣靜靜地躺著狭园,像睡著了一般。 火紅的嫁衣襯著肌膚如雪糊治。 梳的紋絲不亂的頭發(fā)上唱矛,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機與錄音,去河邊找鬼绎谦。 笑死管闷,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的燥滑。 我是一名探鬼主播渐北,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铭拧!你這毒婦竟也來了赃蛛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤搀菩,失蹤者是張志新(化名)和其女友劉穎呕臂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肪跋,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡歧蒋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了州既。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谜洽。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吴叶,靈堂內(nèi)的尸體忽然破棺而出阐虚,到底是詐尸還是另有隱情,我是刑警寧澤蚌卤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布实束,位于F島的核電站,受9級特大地震影響逊彭,放射性物質(zhì)發(fā)生泄漏咸灿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一侮叮、第九天 我趴在偏房一處隱蔽的房頂上張望避矢。 院中可真熱鬧,春花似錦囊榜、人聲如沸谷异。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歹嘹。三九已至,卻和暖如春孔庭,著一層夾襖步出監(jiān)牢的瞬間尺上,已是汗流浹背材蛛。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留怎抛,地道東北人卑吭。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像马绝,于是被迫代替她去往敵國和親豆赏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349