babel入門

Babel

Babel 是一個 JavaScript 編譯器,它能夠將ES6+語法編譯爲瀏覽器支持的ES5語法。要學好babel必須先理解相關的概念,可是你剛起步就去扣這些細節的話,極可能由於babel一些複雜而模糊的概念打擊你的信息。因此咱們先從最簡單的開始,而後深刻。git

最簡單的例子

接下來咱們嘗試按照官方文檔作一個最簡單的例子github

安裝babel-clinpm

npm install --save-dev babel-cli

添加babel編譯命令api

"build": "babel src -d lib"

添加.babelrc配置文件瀏覽器

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

添加JS文件babel

// /src/main.js
const fetch = args => args;
console.log(Promise);

執行 npm run build,咱們發現編譯後的文件沒有發生任何的改變,就是簡單的原樣輸出app

// /lib/main.js
const fetch = args => args;
console.log(Promise);

爲何呢?這是由於在.babelrc文件沒有作任何的配置。咱們一共使用了三個新特性,分別是常量申明const ,箭頭函數=>,新全局變量Promise。接下來我門嘗試將他們編譯爲ES5代碼。async

Alt 來自babel官方文檔

如上圖所示,藉助plugin能夠實現咱們的目標。接下來咱們分別實現它:函數

  • const

原文連接 文檔中說明:工具

babel-plugin-check-es2015-constants 這個插件僅僅是驗證const變量的規則,好比不能重複申明,不可變等特性。

把它編譯爲ES5代碼須要藉助babel-plugin-transform-es2015-block-scoping

咱們設置須要編譯的代碼爲:

const fetch = args => args;
fetch = 55; // 故意寫錯
console.log(Promise);

安裝上面所需的兩個plugin並改寫.babelrc配置:

{
  "presets": [],
  "plugins": [
    "check-es2015-constants",
    "transform-es2015-block-scoping"
  ]
}

編譯過程報錯驗證check-es2015-constants生效
Alt text

咱們修改須要編譯的代碼爲:

const fetch = args => args;
console.log(Promise);

再次編譯成功

var fetch = args => args;
console.log(Promise);

到這裏咱們已經成功的使用插件對const的語法和轉譯(Syntaxtransform)ES5化。是否是很開心呢? 接下來咱們處理箭頭函數=>

  • arrow functions

一樣安裝對應的plugin :

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

這裏延用上面須要compile的源代碼:

var fetch = args => args;
console.log(Promise);

改寫.babelrc配置文件:

{
  "presets": [],
  "plugins": [
    "check-es2015-constants",
    "transform-es2015-block-scoping",
    "transform-es2015-arrow-functions"
  ]
}

編譯結果:

var fetch = function (args) {
  return args;
};
console.log(Promise);

編譯成功,unbelievable, we did it!!!,是否是真的很簡單?先別得意???,還有一個Promise沒有解決呢。有驚喜喲!!!

  • Promise

我有一個問題:我特地將上面的兩個例子放在一塊兒,是由於他們都屬於對新語法Syntax進行編譯,而Promise是做爲一個全局api的存在,在大部分瀏覽器中是不存在這個全局api的,若是讓你來解決這個問題,你會怎麼作?

你的答案:是的,沒錯!就是在不支持Promise的環境中實現Promise,在babel中被稱爲polyfill,注意它和shim的區別哦~

babel-polyfill 文檔最簡單的使用:

npm install --save-dev babel-polyfill
Use it by requiring it at the top of the entry point to your application or in your bundler config. ---在入口文件的最頂層做用域直接引入

到這裏咱們已經完成既定的3個目標,可是你有沒有想過,隨着咱們ES6新特性的增長,plugin的長度也逐漸增長,能夠碰見會有多長?,並且本身去找這些對應的plugin也是比較麻煩的。有沒有傻瓜式的集成方法呢?您接着往下看。

Preset

爲了方便,Babel團隊將一些Plugins集合在一塊兒,並稱之爲preset。因此一個preset是一系列plugin的總和。按照年份劃分:

ES2015/ ES-2016/ES2017 等等,還延伸了stage的概念,詳情請查閱官網。

babel-preset-env

A Babel preset that compiles ES2015+ down to ES5 by automatically determining the Babel plugins and polyfills you need based on your targeted browser or runtime environments.
不須要指定任何的 pluginpolyfill,Babel preset 能夠將ES6+的新語法向下編譯爲ES5代碼,按照你指定的運行環境。具體的配置項請看官網。

Without any configuration options, babel-preset-env behaves exactly the same as babel-preset-latest (or babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017 together).
不須要作任何配置選項,它和babel-preset-latest表現一致,或者說和babel-preset-es2015, babel-preset-es2016, babel-preset-es20174個preset總和一致。

感受是否是很牛逼!下面咱們來嘗試一個例子。爲防止有任何的代碼污染,我卸載全部npm package

最初的樣子:

{
  "name": "babelrc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "babel src -d lib"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {}
}

我安裝時發現@babel/preset-env還處於bate版本,使用時還有一些問題,因此決定仍是使用老版本。

(1)安裝babel-preset-env

Alt text

上圖爲babel-preset-env的一些依賴,主要爲一些helperplugin

爲了驗證出babel-preset-env的是否知足要求,我新增了幾行包括ES6的代碼:

const fetch = args => args;
console.log(Promise);

// 新增一些代碼
class G {

}

let [a, b, c] = [1, 2, 3];

(async () => {
  await console.log(1)
})();

(2)配置.babelrc

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

(3)編譯結果

"use strict";

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var fetch = function fetch(args) {
  return args;
};
console.log(Promise);

// 新增一些代碼

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

var a = 1,
    b = 2,
    c = 3;


_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
  return regeneratorRuntime.wrap(function _callee$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          _context.next = 2;
          return console.log(1);

        case 2:
        case "end":
          return _context.stop();
      }
    }
  }, _callee, undefined);
}))();

我就問還有誰???全部的新語法特性都被成功處理了。

Runtime

官網文檔

babel爲源代碼非實例方法(好比Object.assign)和 babel-runtime/helps下的工具函數自動引用了polyfill。這樣能夠避免全局空間的污染,很是適合用於JS庫和工具包的實現。可是實例方法(好比someString.includes("target"))仍是須要使用babel-polyfill

若是你是新手,你可能沒有注意到,babel編譯時會在每一個文件生成一些須要幫助函數,若是文件比較多,那麼這些重複的代碼會增長編譯後的代碼體積。下面是一個例子:

源代碼

class G {
  
}

.babelrc配置

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

編譯代碼

"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

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

// _classCallCheck就是一個內部生成的幫助函數

爲了優化編譯體積,babel 團隊推出了 babel-plugin-transform-runtime + babel-runtime來解決這個問題。先看一下直觀體驗:

源代碼

class G {
  
}

.babelrc配置

{
  "presets": ["env"],
  "plugins": ["transform-runtime"]
}

編譯後的代碼:

"use strict";

var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var G = function G() {
  (0, _classCallCheck3.default)(this, G);
};

經過對比能夠看出,第二種方案直接從babel-runtime引入babel-runtime/helpers/classCallCheck,避免本身定義,從而減小代碼的體積。

除了這個優勢意外

  • babel-runtime

Alt text

三個主要部分:core.js + helpers+ regenerator

固然除了上面的方法,經過按需引入 polyfillstransforms更能帶來更多的體積優化。

減小對沒必要要瀏覽器的兼容來減小 polyfillstransforms的引入:

{
  "presets": [
    ["env", {
      "targets": {
        // The % refers to the global coverage of users from browserslist
        "browsers": [ ">0.25%", "not ie 11", "not op_mini all"]
      }
    }]
  ]
}

答疑

(1)可能你發現了我並無安裝babel-cli,爲何我能使用babel命令?

由於我在全局安裝了babel

(2) browserslistrc 配置更改以後 babel編譯後的代碼怎麼沒有改變。或者說 browserslistrc 影響babel編譯的具體表現是怎麼樣的?

暫時我也不知道,我正在研究,後面更新。

參考

[1] browserslist
[2] browserslist-example
[3] browserslist-queries

因爲本人表達能力真的不好,表達不夠清楚還望你們多多包涵。

相關文章
相關標籤/搜索