babel7知識

平時在開發的過程當中,咱們可能並不太須要十分了解babel的內容,僅僅知道它可以將新特性的代碼轉換成可以在舊版本瀏覽器中運行的代碼。可是這一次想要趁着本身搭建腳手架的機會去進一步的瞭解babel的知識,因此寫了這篇文章。如下內容是babel 7.4以後的版本,也就是@babel/polyfill被廢棄須要獨立安裝core-jsregenerator-runtime 模塊的版本。npm

babel命令行工具 @babel/cli

@babel/cli是babel的命令行工具,主要提供babel命令。另外還須要安裝@babel/core才能使用babel去編譯。json

npm install --save-dev @babel/core @babel/cli

將命令配置在 package.json 文件的 scripts 字段中:promise

// package.json
"scripts": {
    "compiler": "babel src --out-dir lib --watch"
}

這樣就可以經過npm run compiler來執行編譯,可是babel自己什麼都不作,須要添加插件來幫助babel完成工做。瀏覽器

plugin

babel全部功能都創建在各類的plugin上,使用方式是安裝相應的plugin再去配置文件中去使用。例如箭頭函數轉換插件,
安裝@babel/plugin-transform-arrow-functions,而後在.babelrc配置文件中去指定對應的插件babel

//.babelrc
{
  plugins: ["@babel/plugin-transform-arrow-functions"],
};

而後執行npm run compiler,能夠看到箭頭函數已經被編譯完成async

可是若是咱們每一個功能都去一個個添加對應的plugin會很麻煩,多以咱們就須要preset預設去直接添加一組插件。函數

preset

preset就是一組插件的集合,最經常使用的preset就是@babel/preset-env工具

@babel/preset-env

它的做用是根據目標環境去進行語法轉換和導入對應的polyfillpost

須要注意的是,@babel/preset-env會根據你配置的目標環境,生成插件列表來編譯。默認狀況下,若是你沒有在 Babel 配置文件中(如 .babelrc)設置 targets 或 ignoreBrowserslistConfig,@babel/preset-env 會使用 package.jsonbrowserslist 配置源。ui

咱們能夠模擬生產環境和開發環境的瀏覽器版本

const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

經過設置不一樣瀏覽器環境使用@babel/preset-env去編譯相同代碼,能夠看到最終的結果也會不一樣。

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        // targets: product,
        targets: development,
      },
    ],
  ],
};

babel 只負責對語法進行編譯,好比當咱們寫箭頭函數,babel 會幫你把它編譯成普通函數。可是對一些新的擴展方法,新的類來講babel就不能轉換了。這時就須要去引入polyfillpolyfill的中文意思是墊片,所謂墊片就是墊平不一樣瀏覽器或者不一樣環境下的差別,讓新的內置函數、實例方法等在低版本瀏覽器中也可使用。

polyfill

babel v7.4版以後,須要直接安裝core-jsregenerator-runtime去替代以前的@babel/polyfillcroe-js 提供了 ES五、ES6 規範中新定義的各類對象、方法的polyfill,regenerator-runtime 用來實現 ES6/ES7 中 generators、yield、async 及 await 等相關的 polyfill。

首先,咱們須要安裝他們到生產環境中,由於須要在生產環境中運行其中的polyfill

npm install --save core-js regenerator-runtime

@babel/preset-env的配置項中把useBuiltIns設置成usage,這樣會根據目標瀏覽器環境去引入所須要的polyfill。須要注意點是,設置useBuiltIns還須要同時設置corejs

//.babelrc
const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: development,
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
  ],
};
//index.js
const isHas = [1, 2, 3].includes(2);

const getData = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(100);
    }, 1000);
  });

const main = async () => {
  const res = await getData();
  console.log(res);
};

main();

編譯後的文件:

能夠看到,編譯後的文件中只引入了所用到的polyfill。

useBuiltIns還能夠設置成其餘值,好比entry,這須要在項目入口文件手動引入polyfills,例如@babel/polyfill或者core-js

//.babelrc
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: product,
        useBuiltIns: "entry",
        corejs: 3,
      },
    ],
  ],
};



//index.js
// 入口文件引入core-js
require("core-js");

可是這種方式會引入全量的polyfill。

useBuiltIns默認值爲false,表明每一個文件裏不自動添加polyfill,或不將import "@babel/polyfill"轉換爲單獨的polyfill。

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime能夠重複使用 Babel 注入的幫助程序

在使用@babel/preset-env配合useBuiltIns: usage時,文件中會引入一些輔助方法例如_classCallCheck,當多處文件都使用到class時一樣也會在每一個文件中去引入這些輔助方法,這樣會增大打包體積而且徹底沒有必要屢次去引入一樣的輔助方法。

//index.js
class A {}


//.babelrc.js
const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: product,
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
  ],
};

編譯結果:

"use strict";

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

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

爲了解決這個問題就須要使用@babel/plugin-transform-runtime,使用該插件,全部輔助方法都將引用模塊 @babel/runtime,這樣就能夠避免編譯後的代碼中出現重複的輔助方法,有效減小包體積。

@babel/plugin-transform-runtime須要配合@babel/runtime來使用,@babel/plugin-transform-runtime在開發時使用,最終代碼須要依賴@babel/runtime

npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
//index.js
class A {}

//.babelrc.js
const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: product,
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
  ],
  //使用@babel/plugin-transform-runtime
  plugins: [["@babel/plugin-transform-runtime"]],
};

編譯結果:

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

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

var A = function A() {
  (0, _classCallCheck2.default)(this, A);
};

能夠看到這些輔助方法都是從@babel/runtime中引入。

@babel/plugin-transform-runtime能夠建立一個沙盒環境來避免對全局環境的污染

以前在使用@babel/preset-env編譯promise和includes時會引入core-js中的全局變量或者在對應的原型鏈中添加相應的方法,這樣都形成了全局環境的污染。雖然這對於應用程序或命令行工具是能夠的,可是若是你的代碼是要發佈供他人使用的庫,或者沒法徹底控制代碼運行的環境,則將成爲一個問題。

首先,單獨使用@babel/plugin-transform-runtime只可以處理輔助方法,若是想要去引入polyfill就須要配合@babel/runtime-corejs3使用。

一樣仍是在生產環境安裝@babel/runtime-corejs3

npm install @babel/runtime-corejs3 --save

這裏須要在.babelrc中去除@babel/preset-env配置中關於polyfill的部分以避免與@babel/runtime-corejs3重複。

//index.js
const isHas = [1, 2, 3].includes(2);

const getData = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(100);
    }, 1000);
  });

getData();

//.babelrc.js
module.exports = {
  presets: [["@babel/preset-env"]],
  plugins: [
    [
      "@babel/plugin-transform-runtime",
      {
        corejs: 3,
      },
    ],
  ],
};

編譯結果:

"use strict";

var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

var _setTimeout2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-timeout"));

var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));

var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));

var _context;

var isHas = (0, _includes["default"])(_context = [1, 2, 3]).call(_context, 2);

var getData = function getData() {
  return new _promise["default"](function (resolve, reject) {
    (0, _setTimeout2["default"])(function () {
      resolve(100);
    }, 1000);
  });
};

getData();

能夠看到,使用@babel/plugin-transform-runtime會用一個臨時變量去保存polyfill中的一些值,並非直接去修改原型鏈或者新增Promise方法。

在通常開發中使用@babel/preset-env配合useBuiltIns: usage,在開發第三方庫時使用@babel/plugin-transform-runtime

在上面介紹@babel/plugin-transform-runtime的一些使用時能夠看到,它不只可以處理引入屢次helper輔助方法的問題,並且在只引入所需polyfill時還不會污染全局環境,那還有必要使用@babel/preset-envuseBuiltIns嗎?

其實@babel/plugin-transform-runtime配合@babel/runtime-corejs3引入polyfill有一個很大的不足就是不可以經過設置目標環境去引入所須要的polyfil。,咱們在普通開發時只須要在package.json中的browserslist去設置開發環境和生產環境的瀏覽器版本,而後經過使用@babel/preset-envuseBuiltIns就可以根據不一樣的運行環境去引入適當的polyfill。

可是在開發第三方庫時,不能肯定代碼的運行環境,因此就須要利用@babel/plugin-transform-runtime來保證引入的polyfill不去污染全局環境。

最後總結

通常開發: 經過useBuiltIns: usage去保證引入恰當的polyfill,經過@babel/plugin-transform-runtime保證輔助函數都是引用@babel/runtime`。

const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        //代替browserslist設置瀏覽器版本
        targets: product,
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
  ],
  plugins: [["@babel/plugin-transform-runtime"]],
};

參考文章

相關文章
相關標籤/搜索