babel只能轉義ES6語法,好比箭頭函數,可是遇到ES6新增的api就無能爲力了,好比Promise和includes。對於這些新增的api,須要polyfill去作兼容。babel提供了兩個plugin來處理這些api的polyfill。node
若是使用preset-env來處理polyfill,須要安裝@babel/polyfill。webpack
@babel/preset-env經過配置項,可以智能的幫你處理ES6的語法。它經過讀取項目中的browserslist配置,來決定須要對哪些語法進行處理。一個配置的例子es6
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": "58",
"ie": "11"
}
"useBuiltIns": "entry",
"modules": false,
}
]
]
}
複製代碼
用來配置須要支持的的環境,不只支持瀏覽器,還支持node。web
若是沒有配置targets選項,就會讀取項目中的browserslist配置項。chrome
默認值是false,若是preset-env中包含的plugin支持loose的設置,那麼能夠經過這個字段來作統一的設置。api
"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false
,默認值是autopromise
用來轉換ES6的模塊語法。若是使用false,將不會對文件的模塊語法進行轉化。瀏覽器
若是要使用webpack中的一些新特性,好比tree shaking 和 sideEffects,就須要設置爲false,對ES6的模塊文件不作轉化,由於這些特性只對ES6的模塊有效。bash
"usage" | "entry" | false
,默認值是falsebabel
這個配置項主要是用來處理@babel/polyfill。
其餘的一些配置項能夠看 官方文檔
這個插件能夠經過一些helper函數的注入來減小語法轉換函數的開銷。
const c = {...b};
複製代碼
經過babel編譯之後,對象的解構語法會被編譯爲下面的文件
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var c = _extends({}, b);
複製代碼
若是不少文件都用到了解構語法,那麼每一個文件都會生成一個相同的_extends方法,@babel/plugin-transform-runtime會使用helper函數來處理對應的語法,避免這種重複定義方法的問題,使用這個插件後,會生成下面的文件
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
var c = (0, _objectSpread2.default)({}, b);
複製代碼
對插件進行配置
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
複製代碼
babel7相比於babel6,刪除了polyfill和useBuiltIns這兩個配置。
boolean or number
,默認值是false
e.g. ['@babel/plugin-transform-runtime', { corejs: 2 }]
當設置爲false時,只對語法進行轉換,不對api進行處理
當設置爲2的時候,須要安裝@babel/runtime-corejs2,這時會對api進行處理。這裏須要注意,不能polyfill Array.includes這種須要重寫property的api,這種會污染全局變量,須要在項目裏使用@babel/polyfill來處理。
默認值是true,用來開啓是否使用helper函數來重寫語法轉換的函數。
默認值是false,是否對文件使用ES的模塊語法,使用ES的模塊語法能夠減小文件的大小。
// useESModules:false
exports.__esModule = true;
exports.default = function(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
複製代碼
// useESModules:true
export default function(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
複製代碼
@babel/plugin-transform-runtime默認狀況下安裝@babel/runtime這個庫,即corejs爲false時使用;當corejs設置爲2時,須要安裝使用@babel/runtime-corejs2。runtime和runtime-corejs2這兩個庫惟一的區別是,corejs2這個庫增長了對core-js這個庫的依賴,而core-js是用來對ES6各個語法polyfill的庫,因此在corejs爲false的狀況下,只能作語法的轉換,並不能polyfill任何api。
polyfill和runtime均可以用來對api進行墊片處理,可是二者還有必定的不一樣。使用polyfill的時候,會污染全局變量;而runtime的時候,會使用局部變量來處理,不會污染全局變量。這也是爲何runtime沒法處理原型上的api的緣由,由於要模擬這些api,必需要污染全局變量。
@babel/polyfill和@babel/runtime-corejs2都使用了core-js(v2)這個庫來進行api的處理。
這個庫有兩個核心的文件夾,分別是library和modules。runtime使用library這個文件夾,polyfill使用modules這個文件夾。
library和modules包含的文件基本相同,最大的不一樣是_export.js這個文件。
core-js/modules/_exports.js
文件以下
var global = require('./_global');
var core = require('./_core');
var hide = require('./_hide');
var redefine = require('./_redefine');
var ctx = require('./_ctx');
var PROTOTYPE = 'prototype';
var $export = function (type, name, source) {
var IS_FORCED = type & $export.F;
var IS_GLOBAL = type & $export.G;
var IS_STATIC = type & $export.S;
var IS_PROTO = type & $export.P;
var IS_BIND = type & $export.B;
var target = IS_GLOBAL ? global : IS_STATIC ? global[name] || (global[name] = {}) : (global[name] || {})[PROTOTYPE];
var exports = IS_GLOBAL ? core : core[name] || (core[name] = {});
var expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {});
var key, own, out, exp;
if (IS_GLOBAL) source = name;
for (key in source) {
// contains in native
own = !IS_FORCED && target && target[key] !== undefined;
// export native or passed
out = (own ? target : source)[key];
// bind timers to global for call from export context
exp = IS_BIND && own ? ctx(out, global) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;
// extend global
if (target) redefine(target, key, out, type & $export.U);
// export
if (exports[key] != out) hide(exports, key, exp);
if (IS_PROTO && expProto[key] != out) expProto[key] = out;
}
};
global.core = core;
// type bitmap
$export.F = 1; // forced
$export.G = 2; // global
$export.S = 4; // static
$export.P = 8; // proto
$export.B = 16; // bind
$export.W = 32; // wrap
$export.U = 64; // safe
$export.R = 128; // real proto method for `library`
module.exports = $export;
複製代碼
core-js/library/_exports.js
文件以下
var global = require('./_global');
var core = require('./_core');
var ctx = require('./_ctx');
var hide = require('./_hide');
var has = require('./_has');
var PROTOTYPE = 'prototype';
var $export = function (type, name, source) {
var IS_FORCED = type & $export.F;
var IS_GLOBAL = type & $export.G;
var IS_STATIC = type & $export.S;
var IS_PROTO = type & $export.P;
var IS_BIND = type & $export.B;
var IS_WRAP = type & $export.W;
var exports = IS_GLOBAL ? core : core[name] || (core[name] = {});
var expProto = exports[PROTOTYPE];
var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE];
var key, own, out;
if (IS_GLOBAL) source = name;
for (key in source) {
// contains in native
own = !IS_FORCED && target && target[key] !== undefined;
if (own && has(exports, key)) continue;
// export native or passed
out = own ? target[key] : source[key];
// prevent global pollution for namespaces
exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key]
// bind timers to global for call from export context
: IS_BIND && own ? ctx(out, global)
// wrap global constructors for prevent change them in library
: IS_WRAP && target[key] == out ? (function (C) {
var F = function (a, b, c) {
if (this instanceof C) {
switch (arguments.length) {
case 0: return new C();
case 1: return new C(a);
case 2: return new C(a, b);
} return new C(a, b, c);
} return C.apply(this, arguments);
};
F[PROTOTYPE] = C[PROTOTYPE];
return F;
// make static versions for prototype methods
})(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;
// export proto methods to core.%CONSTRUCTOR%.methods.%NAME%
if (IS_PROTO) {
(exports.virtual || (exports.virtual = {}))[key] = out;
// export proto methods to core.%CONSTRUCTOR%.prototype.%NAME%
if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out);
}
}
};
// type bitmap
$export.F = 1; // forced
$export.G = 2; // global
$export.S = 4; // static
$export.P = 8; // proto
$export.B = 16; // bind
$export.W = 32; // wrap
$export.U = 64; // safe
$export.R = 128; // real proto method for `library`
module.exports = $export;
複製代碼
能夠看出,library下的這個$export方法,會實現一個wrapper函數,防止污染全局變量。
var p = new Promise();
// @babel/polyfill
require("core-js/modules/es6.promise");
var p = new Promise();
// @babel/runtime-corejs2
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
var a = new _promise.default();
複製代碼
從上面這個例子能夠看出,對於Promise這個api,@babel/polyfill引用了core-js/modules中的es6.promise.js文件,由於是對全局變量進行處理,因此賦值語句不用作處理;@babel/runtime-corejs2會生成一個局部變量_promise,而後把Promise都替換成_promise,這樣就不會污染全局變量了。
在項目開發的時候,使用@babel/polyfill;在開發庫的時候,使用runtime。
若是在庫開發的時候,用到了一些新的api,能夠選擇不要處理,把選擇權留給開發者,讓開發者在項目中使用@babel/polyfill去處理。
由於在項目中都會排除node_modules裏面的js文件,加快項目的編譯速度。出於這個考慮,不建議使用把env的useBuiltIns設置爲usage,這樣可能會致使node_modules裏面的庫使用了某些須要polyfill的api,可是咱們又沒有引入對應的polyfill文件,在某些環境會出現bug。
在項目裏,建議使用entry這個配置,並配合browserslist,對於某些已經被目標環境支持的api不用引入對應的polyfill文件,減小項目的文件體積。