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文件,减小项目的文件体积。