Babel的使用

Babel介紹

Babel 把用最新標準編寫的 JavaScript 代碼向下編譯成能夠在今天隨處可用的版本。 這一過程叫作「源碼到源碼」編譯, 也被稱爲轉換編譯。javascript

15 年 11 月,Babel 發佈了 6.0 版本。相較於前一代 Babel 5,新一代 Babel 更加模塊化, 將全部的轉碼功能以插件的形式分離出去,默認只提供 babel-core。本來只須要裝一個 babel ,如今必須按照本身的需求配置,靈活性提升的同時也提升了使用者的學習成本。html

npm i babel
已經棄用,你能下載到的僅僅是一段 console.warn,告訴你 babel 6 再也不以大雜燴的形式提供轉碼功能了。java

例如,Babel 可以將新的 ES2015 箭頭函數語法:node

let fun = () => console.log('babel')

轉譯爲:react

"use strict";
var fun = function fun() {
  return console.log('babel');
};

不過 Babel 的用途並不止於此,它支持語法擴展,能支持像 React 所用的 JSX 語法,更重要的是,Babel 的一切都是簡單的插件,誰均可以建立本身的插件,利用 Babel 的所有威力去作任何事情。
再進一步,Babel 自身被分解成了數個核心模塊,任何人均可以利用它們來建立下一代的 JavaScript 工具。webpack

使用 Babel

babel-cli

Babel 的 CLI 是一種在命令行下使用 Babel 編譯文件的簡單方法。git

讓咱們先全局安裝它來學習基礎知識。es6

$ npm install --global babel-cli

咱們能夠這樣來編譯咱們的第一個文件:github

$ babel my-file.js

這將把編譯後的結果直接輸出至終端。使用 --out-file 或着 -o 能夠將結果寫入到指定的文件。web

$ babel example.js --out-file compiled.js
# 或
$ babel example.js -o compiled.js

若是咱們想要把一個目錄整個編譯成一個新的目錄,可使用 --out-dir 或者 -d。.

$ babel src --out-dir lib
# 或
$ babel src -d lib

babel-core

若是你須要以編程的方式來使用 Babel,可使用 babel-core 這個包。

babel-core 的做用是把 js 代碼分析成 ast ,方便各個插件分析語法進行相應的處理。有些新語法在低版本 js 中是不存在的,如箭頭函數,rest 參數,函數默認值等,這種語言層面的不兼容只能經過將代碼轉爲 ast,分析其語法後再轉爲低版本 js。
首先安裝 babel-core。.

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

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

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

若是是文件的話,可使用異步 api:

babel.transformFile("filename.js", options, function(err, result) {
  result; // => { code, map, ast }
});

或者是同步 api:

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

其餘用法

還能夠經過babel-registerbabel-node使用Babel,但因爲這兩種用法不適合生產環境故省略。

配置 Babel

你或許已經注意到了,目前爲止經過運行 Babel 本身咱們並沒能「翻譯」代碼,而僅僅是把代碼從一處拷貝到了另外一處。緣由就是從Babel 6之後, 默認的插件被移除, 若是沒有指定一個插件,Babel將會原樣輸出, 不會進行編譯。

你能夠經過安裝插件(plugins)或預設(presets,也就是一組插件)來指示 Babel 去作什麼事情。

插件只是單一的功能,例如

  • es2015-arrow-functions

  • es2015-classes

  • es2015-for-of

  • es2015-spread

如下是安裝箭頭函數的插件方式

npm install --save-dev babel-plugin-transform-es2015-arrow-functions

若是咱們一個一個引人功能單一的插件的話顯得特別麻煩,一般咱們用的更多的是預設。插件和預設一般寫入到配置文件中。能夠將配置寫入package.json的‘babel’屬性裏,或者是一個單獨的.babelrc文件。

.babelrc

在咱們告訴 Babel 該作什麼以前,你須要作的就是在項目的根路徑下建立 .babelrc 文件。而後輸入如下內容做爲開始:

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

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

babel-preset-es2015

預設 babel-preset 系列打包了一組插件,相似於餐廳的套餐。如 babel-preset-es2015 打包了 es6 的特性,babel-preset-stage-0 打包處於 strawman 階段的語法

咱們須要安裝 "es2015" Babel 預設:

$ npm install --save-dev babel-preset-es2015

咱們修改 .babelrc 來包含這個預設。

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

一樣的,還有babel-preset-2016babel-preset-2017

babel-preset-latest

latest是一個特殊的presets,包括了es2015,es2016,es2017的插件(目前爲止,之後有es2018也會包括進去)。即老是包含最新的編譯插件。

babel-preset-env

上面提到的各類preset的問題就是: 它們都太」重」了, 即包含了過多在某些狀況下不須要的功能. 好比, 現代的瀏覽器大多支持ES6的generator, 可是若是你使用babel-preset-es2015, 它會將generator函數編譯爲複雜的ES5代碼, 這是沒有必要的。但使用babel-preset-env, 咱們能夠聲明環境, 而後該preset就會只編譯包含咱們所聲明環境缺乏的特性的代碼,所以也是比較推薦的方式。

安裝babel-preset-env

npm install babel-preset-env --save-dev

添加配置

{
  "presets": ["env"]
}

當沒有添加任何的配置選項時,babel-preset-env默認行爲是和babel-preset-latest是同樣的。
下面咱們經過一些例子來看babel-preset-env的配置是如何使用的:

  • 指定支持主流瀏覽器最新的兩個版本以及IE 7+:

"presets": [
    [
      "env",
      {
        "targets": {
          "browsers": ["last 2 versions", "ie >= 7"]
        }
      }
    ]
  ]
}
  • 支持超過市場份額5%的瀏覽器:

"targets": {
  "browsers": "> 5%"
}
  • 某個固定版本的瀏覽器:

"targets": {
  "chrome": 56
}

更多的配置請查看官方文檔

babel-preset-stage-x

官方預設(preset), 有兩種,一個是按年份(babel-preset-2017),一個是按階段(babel-preset-stage-0)。 這主要是根據TC39 委員會ECMASCRPIT 發佈流程來制定的。TC39 委員會決定,從2016年開始,每一年都會發佈一個版本,它包括每一年期限內完成的全部功能,同時ECMAScript的版本號也按年份編制,就有了ES2016, ES2017。因此也就有了babel-present-2016, babel-preset-2017, 對每年新增的語法進行轉化。babel-preset-latest 就是把全部es2015, es2016, es2017 所有包含在一塊兒了。

最終在階段 4 被標準正式採納。
如下是4 個不一樣階段的(打包的)預設:

  • babel-preset-stage-0

  • babel-preset-stage-1

  • babel-preset-stage-2

  • babel-preset-stage-3

注意 stage-4 預設是不存在的由於它就是上面的 es2017 預設。

以上每種預設都依賴於緊隨的後期階段預設,數字越小,階段越靠後,存在依賴關係。也就是說stage-0是包括stage-1的,以此類推。也就是說這些stage包含的特性是比latest更新的特性但還未被寫入標準進行發佈。

使用的時候只須要安裝你想要的階段就能夠了:

$ npm install --save-dev babel-preset-stage-2

而後添加進你的 .babelrc 配置文件。可是要注意若是沒有提供es2017相關的預設,preset-stage-X 這種階段性的預設也不能用。

執行 Babel 生成的代碼

Babel 幾乎能夠編譯全部時新的 JavaScript 語法,但對於 APIs 來講卻並不是如此。例如: Promise、Set、Map 等新增對象,Object.assign、Object.entries等靜態方法。

爲了達成使用這些新API的目的,社區又有2個實現流派:babel-polyfill和babel-runtime+babel-plugin-transform-runtime。

這兩個模塊功能幾乎相同,就是轉碼新增 api,模擬 es6 環境,但實現方法徹底不一樣。babel-polyfill 的作法是將全局對象統統污染一遍,好比想在 node 0.10 上用 Promise,調用 babel-polyfill 就會往 global 對象掛上 Promise 對象。對於普通的業務代碼沒有關係,但若是用在模塊上就有問題了,會把模塊使用者的環境污染掉。

babel-runtime 的作法是本身手動引入 helper 函數,仍是上面的例子,const Promise = require('babel-runtime/core-js/promise') 就能夠引入 Promise。

但 babel-runtime 也有問題,第一,很不方便,第二,在代碼中中直接引入 helper 函數,意味着不能共享,形成最終打包出來的文件裏有不少重複的 helper 代碼。因此,babel 又開發了 babel-plugin-transform-runtime,這個模塊會將咱們的代碼重寫,如將 Promise 重寫成 _Promise(只是打比方),而後引入_Promise helper 函數。這樣就避免了重複打包代碼和手動引入模塊的痛苦。

babel-polyfill

爲了解決這個問題,咱們使用一種叫作 Polyfill(代碼填充,也可譯做兼容性補丁) 的技術。 簡單地說,polyfill便是在當前運行環境中用來複制(意指模擬性的複製,而不是拷貝)尚不存在的原生 api 的代碼。能讓你提早使用還不可用的 APIs,Array.from 就是一個例子。
Babel 用了優秀的 core-js 用做 polyfill,而且還有定製化的 regenerator 來讓 generators(生成器)和 async functions(異步函數)正常工做。
要使用 Babel polyfill,首先用 npm 安裝它:

$ npm install --save babel-polyfill

而後只須要在文件頂部導入 polyfill 就能夠了:

import "babel-polyfill";

babel-runtime

與 babel-polyfill 同樣,babel-runtime 的做用也是模擬 ES2015 環境。只不過,babel-polyfill 是針對全局環境的,引入它,咱們的瀏覽器就好像具有了規範裏定義的完整的特性 – 雖然原生並未實現。
babel-runtime 更像是分散的 polyfill 模塊,咱們能夠在本身的模塊裏單獨引入,好比 require(‘babel-runtime/core-js/promise’) ,它們不會在全局環境添加未實現的方法,只是,這樣手動引用每一個 polyfill 會很是低效。咱們藉助 Runtime transform 插件來自動化處理這一切。
經過安裝 babel-plugin-transform-runtimebabel-runtime 來開始。

$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime

而後更新 .babelrc

{
    "plugins": [
      "transform-runtime",
      "transform-es2015-classes"
    ]
  }

如今,Babel 會把這樣的代碼:

class Foo {
  method() {}
}

編譯成:

import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";

let Foo = function () {
  function Foo() {
    _classCallCheck(this, Foo);
  }

  _createClass(Foo, [{
    key: "method",
    value: function method() {}
  }]);

  return Foo;
}();

這樣就不須要把 _classCallCheck_createClass 這兩個助手方法放進每個須要的文件裏去了。

那何時用 babel-polyfill 何時用 babel-runtime 呢?若是你不介意污染全局變量(如上面提到的業務代碼),放心大膽地用 babel-polyfill ;而若是你在寫模塊,爲了不污染使用者的環境,沒的選,只能用 babel-runtime + babel-plugin-transform-runtime

options

不少預設和插件都有選項用於配置他們自身的行爲。 例如,不少轉換器都有「寬鬆」模式,經過放棄一些標準中的行爲來生成更簡化且性能更好的代碼。

要爲插件添加選項,只須要作出如下更改:

{
    "plugins": [
      "transform-runtime",
-     "transform-es2015-classes",
+     ["transform-es2015-classes", { "loose": true }]
    ]
}

plugins/presets排序:

  • 具體而言,plugins優先於presets進行編譯。

  • plugins按照數組的index增序(從數組第一個到最後一個)進行編譯。

  • presets按照數組的index倒序(從數組最後一個到第一個)進行編譯。由於做者認爲大部分會把presets寫成["es2015", "stage-0"]。具體細節能夠看這個。

webpack 中定義 babel-loader

不多有大型項目僅僅須要 babel,通常都是 babel 配合着 webpack 或 glup 等編譯工具一塊兒上的。
爲了顯出 babel 的能耐,咱們分別配個用 babel-polyfillbabel-runtime 、支持 react 的webpack.config.js
先來配使用 babel-runtime 的:
首先安裝:

npm install babel-loader babel-core babel-preset-es2015 babel-plugin-transform-runtime webpack --save-dev
npm install babel-runtime --save

而後配置

module: {
  loaders: [{
    loader: 'babel',
    test: /\.jsx?$/,
    include: path.join(__dirname, 'src'),
    query: {
      plugins: ['transform-runtime'],
      presets: [
        ["env", {
          "targets": {
            "chrome": 52
          },
          "modules": false,
          "loose": true
        }],
        'stage-2',
        'react'
      ],
    }
  }]
}

須要注意的是,babel-runtime 雖然沒有出如今配置裏,但仍然須要安裝,由於 transform-runtime 依賴它。
再來個 babel-polyfill 的:

entry: [
  'babel-polyfill',
  'src/index.jsx',
],

module: {
  loaders: [{
    loader: 'babel',
    test: /\.jsx?$/,
    include: path.join(__dirname, 'src'),
    query: {
      presets: [
        ["env", {
          "targets": {
            "chrome": 52
          },
          "modules": false,
          "loose": true
        }],
        'stage-2',
        'react',
      ],
    }
  }]
}

參考文檔:
http://babeljs.io/
https://github.com/thejamesky...
https://excaliburhan.com/post...
https://icyfish.me/2017/05/18...

相關文章
相關標籤/搜索