babel-runtime和babel-polyfill的介紹和使用

chup發表於2019-05-07

Babel編譯轉碼的範圍

Babel預設只轉換新的JavaScript語法,而不轉換新的API。 例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全域性物件,及一些定義在全域性物件上的方法(比如 Object.assign)都不會轉譯。 如果想使用這些新的物件和方法,則需要為當前環境提供一個polyfill。

babel-polyfill

目前最常用的配合Babel一起使用的polyfill是babel-polyfill,它會載入整個"polyfill庫",針對新的API進行處理,並且在程式碼中插入一些幫助函式。

比如:E6物件擴充套件的字面量定義時使用屬性名錶達式

const key = 'babel'
const obj = {
    [key]: 'polyfill',
}
複製程式碼

使用babel-polyfill配合轉碼後,程式碼會變成這樣

function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });
    } else {
        obj[key] = value;
    }
    return obj;
}
var key = 'babel';
var obj = _defineProperty({}, key, Object.assign({}, { key: 'polyfill' }));
複製程式碼

babel-runtime

babel-polyfill解決了Babel不轉換新API的問題,但其直接在程式碼中插入幫助函式,會導致汙染了全域性環境,並且不同的程式碼檔案中包含重複的程式碼,導致編譯後的程式碼體積變大。 (比如:上述的幫助函式_defineProperty有可能在很多的程式碼模組檔案中都會被插入)

為了解決這個問題,Babel提供了babel-runtime用以提供編譯模組的工具函式, 啟用外掛babel-plugin-transform-runtime後,Babel就會使用babel-runtime下的工具函式,上述的程式碼就會變成這樣

var _defineProperty2 = __webpack_require__("./node_modules/babel-runtime/helpers/defineProperty.js");
var _defineProperty3 = _interopRequireDefault(_defineProperty2);
var _assign = __webpack_require__("./node_modules/babel-runtime/core-js/object/assign.js");
var _assign2 = _interopRequireDefault(_assign);
function _interopRequireDefault(obj) { 
    return obj && obj.__esModule ? obj : { default: obj }; 
}
var key = 'babel';
var obj = (0, _defineProperty3.default)(
            {}, key, (0, _assign2.default)({}, { key: 'polyfill' })
          );

複製程式碼

上述轉換後的程式碼中_defineProperty幫助函式是通過babel-runtime下的模組引用的, 同時Object.assign也變成了模組引用, 這樣可以避免自行引入polyfill時導致的汙染全域性名稱空間的問題。

配置

使用babel-polyfill需要額外安裝babel-polyfill依賴包, 然後在webpack配置檔案中的入口或者公共模組中加入’babel-polyfill’即可,程式碼如下:

entry: {
    common: [
        `babel-polyfill`,
        `whatwg-fetch`,
        `react`,
        `react-dom`,
        `redux`,
        `react-redux`,
        `js-cookie`,
    ],
},
複製程式碼

使用babel-runtime需要額外安裝babel-runtime和babel-plugin-transform-runtime依賴包, 然後在.babelrc配置檔案中啟用transform-runtime, 程式碼如下:

{
    "presets": [
        "es2015",
        "react",
        "stage-0"
    ],
    "plugins": [
        "transform-runtime"
    ]
}
複製程式碼

比較

babel-polyfill與babel-runtime相比雖然有各種缺點,但某些情況下babel-polyfill仍然不能被babel-runtime替代, 例如,程式碼:[1, 2, 3].includes(3),Object.assign({}, {key: 'value'}),Array,Object以及其他”例項”下es6的方法,babel-runtime是無法支援的, 因為babel-runtime只支援到static的方法。

思考 babel-runtime 為什麼適合 JavaScript 庫和工具包的實現?

  1. 避免 babel 編譯的工具函式在每個模組裡重複出現,減小庫和工具包的體積;

  2. 在沒有使用 babel-runtime 之前,庫和工具包一般不會直接引入 polyfill。否則像 Promise 這樣的全域性物件會汙染全域性名稱空間,這就要求庫的使用者自己提供 polyfill。這些 polyfill 一般在庫和工具的使用說明中會提到,比如很多庫都會有要求提供 es5 的 polyfill。在使用 babel-runtime 後,庫和工具只要在 package.json 中增加依賴 babel-runtime,交給 babel-runtime 去引入 polyfill 就行了;

總結:

1.具體專案還是需要使用 babel-polyfill,只使用 babel-runtime 的話,例項方法不能正常工作(例如 "foobar".includes("foo"));

2.JavaScript 庫和工具可以使用 babel-runtime,在實際專案中使用這些庫和工具,需要該專案本身提供 polyfill;

vue-cli腳手架中使用

babel-polyfill 在main.js中引用babel-polyfill

import 'babel-polyfill'
複製程式碼

bable-runtime

在.babelrc檔案中使用

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}

複製程式碼

相關文章