【前端詞典】關於 Babel 你必須知道的

前言

我第一次打開搜索引擎查詢關於 Babel 的資料時,出現的居然是關於 Babel 的傳說。後來我花了小一天的時間去了解這個傳說(來自《舊約聖經》)。前端

Babel Tower 是全人類聯手建造的一個建築,人們決心協力修建一座通天高塔。 由於人們內心少了對上帝的敬畏,多了爲本身歌功頌德的功利。上帝不但願這個奇觀建成,因而讓人們分化成不一樣的語言,令其不能交流。以後,由於溝通不順暢,工程被迫放棄,並且人類今後再也不團結,由於語言不通而分化成不一樣部落,並因爲溝通問題,常常發生戰亂,所以再無力撼動上帝的權威。node

瞭解完這個神話以後,我後面就好好的去了解了一些有關 Babel 的知識,下面就是正題。webpack

內容已經發布在 gitHub 了,歡迎圍觀 Star,更多文章都在 gitHub。git

什麼是 Babel

Babel 官方文檔: babeljs.io/github

咱們知道各個瀏覽器對 JavaScript 版本的支持各不相同,有不少優秀的新語法都不能直接在瀏覽器中運行。爲了解決這個「溝通不順暢」的問題,因此就有了 Babel,Babel 的出現使得咱們能夠無須顧忌的去使用 ES6+ 的語法。web

Babel is a JavaScript compiler.json

這也是爲什麼咱們必須使用 ES6+ 語法的前提條件。api

若是你如今還不清楚 ES6+ 語法的話,趕快學習去吧,要否則你就只能回家繼承幾十億的家產啦。瀏覽器

Babel 如何編譯

先看下面這張圖: bash

你會發現 ES6 的語法確實被編譯成瀏覽器能夠識別的版本了,你是否是也在問這事怎麼作到的呢?

babel 編譯的階段

babel 總共分爲三個階段:解析,轉換,生成。

咱們須要知道如今 babel 自己是不具有這種轉化功能,提供這些轉化功能的是一個個 plugin。因此咱們沒有配置任何 plugin 的時候,通過 Babel 輸出的代碼是沒有改變的。

Plugin —— transform 的載體

Babel 自 6.0 起,就再也不對代碼進行轉換。如今只負責圖中的 parse 和 generate 流程,轉換代碼的 transform 過程全都交給插件去作。

例子:

// 模板字面量
const name = '小生方勤';
let hello = `hello ${name}`;
複製代碼

上面是一個簡單的模板字面量的例子,咱們清楚這個是 ES6 的新特性,在不支持 ES6 的運行平臺這段代碼是會報錯的,因此咱們須要 Babel 來將其編譯成 ES5 的代碼。

因此咱們須要以下來配置 babel:

// .babelrc 文件
{ 
  "plugins": [
    "transform-es2015-template-literals"  // 轉譯模版字符串的 plugins
  ],
  "presets": ["env", "stage-2"]
}
複製代碼

preset(即一組預先設定的插件)

preset: babel 插件集合的預設,包含某些插件 plugin。顯然像上面那樣一個一個配置插件會很是的麻煩,爲了方便,babel 爲咱們提供了一個配置項叫作 persets(預設)。

當前 babel 推薦使用 babel-preset-env 替代 babel-preset-es201X ,env 的支持範圍更廣,包含es201X 的全部語法編譯,而且它能夠根據項目運行平臺的支持狀況自行選擇編譯版本。

plugins 與 presets 同時存在的執行順序

  1. 先執行 plugins 的配置項,再執行 Preset 的配置項;
  2. plugins 配置項,按照聲明順序執行;
  3. Preset 配置項,按照聲明逆序執行。

列入如下代碼的執行順序爲:

  1. transform-es2015-template-literals
  2. stage-2
  3. env
// .babelrc 文件
{ 
    "plugins": [
        "transform-es2015-template-literals",  // 轉譯模版字符串的 plugins
    ],
    "presets": [
        ["env", {
            // 是否自動引入 polyfill,開啓此選項必須保證已經安裝了 babel-polyfill
            // 「usage」 | 「entry」 | false, defaults to false.
            "useBuiltIns": "usage"
        }], "stage-2"]
}
複製代碼

這裏講一講 useBuiltIns 配置

咱們可能在全局引入 babel-polyfill,這樣打包後的整個文件體積必然是會變大的。

可是經過設置 "useBuiltIns": "usage" 可以把 babel-polyfill 中你須要用到的部分提取出來,不須要的去除。

useBuiltIns 參數說明:

  1. false: 不對 polyfills 作任何操做
  2. entry: 根據 target 中瀏覽器版本的支持,將 polyfills 拆分引入,僅引入有瀏覽器不支持的 polyfill
  3. usage(新):檢測代碼中 ES6/7/8 等的使用狀況,僅僅加載代碼中用到的 polyfills

Babel 相關模塊簡要說明

瞭解過 Babel 的同窗,是否也以爲的模塊有點多呢?我開始學習的時候就有這種感受。其實每一個模塊是各司其職的。

babel-core(核心)

這個模塊是最能顧名思義的了,即 babel 的核心模塊。babel 的核心 api 都在這個模塊中。也就是這個模塊會把咱們寫的 js 代碼抽象成 AST 樹;而後再將 plugins 轉譯好的內容解析爲 js 代碼。

具體怎麼工做的這裏就不詳細說了,由於我也不知道。

babel-cli

babel-cli 官方文檔:babeljs.io/docs/en/bab…

babel-cli 是一個經過命令行對 js 文件進行轉換的工具。

固然咱們通常不會使用到這個模塊,由於通常咱們都不會手動去作這個工做,這個工做基本都集成到模塊化管理工具中去了,好比 webpack、Rollup 等。

簡單使用(須要先安裝 babel-cli):

babel test.js -o compiled.js
複製代碼

babel-node

babel-node 是 babel-cli 的一部分,因此它在安裝 babel-cli 的時候也同時安裝了。

它使 ES6+ 能夠直接運行在 node 環境中。

babel-polyfill(內部集成了 core-js 和 regenerator)

babel 對一些新的 API 是沒法轉換,好比 Generator、Set、Proxy、Promise 等全局對象,以及新增的一些方法:includes、Array.form 等。因此這個時候就須要一些工具來爲瀏覽器作這個兼容。

官網的定義:babel-polyfill 是爲了模擬一個完整的 ES6+ 環境,旨在用於應用程序而不是庫/工具。

babel-polyfill 主要有兩個缺點:

  1. 使用 babel-polyfill 會致使打出來的包很是大,不少其實沒有用到,對資源來講是一種浪費。
  2. babel-polyfill 可能會污染全局變量,給不少類的原型鏈上都做了修改,這就有不可控的因素存在。

由於上面兩個問題,因此在 Babel7 中增長了 babel-preset-env,咱們設置 "useBuiltIns": "usage" 這個參數值就能夠實現按需加載 babel-polyfill 啦。

babel-runtime & babel-plugin-transform-runtime

在使用 Babel6 的時候, .babelrc 文件中會使用 babel-plugin-transform-runtime,而 package.json 中的 dependencies 同時包含了 babel-runtime,由於在使用 babel-plugin-transform-runtime 的時候必須把 babel-runtime 當作依賴。

.babelrc 配置:

{
  "presets": [
    ["env"]
  ],
  "plugins": [
    ["transform-runtime", {
      "helpers": false, // defaults to true
      "polyfill": false, // defaults to true
      "regenerator": true, // defaults to true
      "moduleName": "babel-runtime" // defaults to "babel-runtime"
    }]
  ]
}
複製代碼

咱們在啓用插件 babel-plugin-transform-runtime 後,Babel 就會使用 babel-runtime 下的工具函數,將一些瀏覽器不能支持的特性重寫,而後在項目中使用。

babel-runtime 內部也集成了 core-js、 regenerator、helpers 等

因爲採用了沙盒機制,這種作法不會污染全局變量,也不會去修改內建類的原型,因此會有重複引用的問題。

如今最好的實踐應該是在 babel-preset-env 設置 "useBuiltIns": "usage",按需引入 polyfill。

三種方案對比

方案 優勢 缺點
@babel/runtime & @babel/plugin-transform-runtime 按需引入, 打包體積小 不能兼容實例方法
@babel/polyfill 完整模擬 ES2015+ 環境 打包體積過大, 污染全局對象和內置的對象原型
@babel/preset-env 按需引入, 可配置性高 小生不知 -_-

babel7 的一些變化

preset 的變動:

淘汰 es201x,刪除 stage-x,推薦 env

若是你還在使用 es201x,官方建議使用 env 進行替換。淘汰並非刪除,只是不推薦使用

但 stage-x 是直接被刪了,也就是說在 babel7 中使用 es201X 是會報錯的。

包名稱變化

babel 7 的一個重大變化,把全部 babel-* 重命名爲 @babel/*,

例如:

  1. babel-cli —> @babel/cli。
  2. babel-preset-env —> @babel/preset-env

低版本 node 再也不支持

babel 7.0 開始再也不支持 nodejs 0.10, 0.12, 4, 5 這四個版本,至關於要求 nodejs >= 6。

還有一些包從其餘包獨立出來的變化等等

關於如何配置 Babel

接下來我會專門寫一篇關於開發環境配置的問題,也就是本身完成腳手架的功能,因此這裏就不提如何配置 Babel 啦。

最後

若是你想進【大前端交流羣】關注公衆號點擊「交流加羣」添加機器人自動拉你入羣。關注我第一時間接收最新干貨。

相關文章
相關標籤/搜索