摘要:对于初学者而言,对于Babel的使用可能一直稀里糊涂,对于很多参数的配置和区别也都是一知半解。本文将通过一些使用 babel-runtime 和 babel-polyfill 的简单例子 ,帮助读者区分理解两者的使用场景。
前言
我们知道Babel是一个通用型的JS编译器,通过Babel我们可以把最新标准编写的JS代码向下编译成兼容各种浏览器或Node的通用版本。你可以通过安装预设(presets,一系列同类插件组合) 或 插件(plugins) 告诉Babel应该如何进行代码转译,例如:@babel/preset-env
(转译 ES6 ~ ES9
的语法)、 @babel/preset-react
(转译React
)。
@babel/preset-env
介绍
preset-env是ES语法插件的合集,官方已经不再推荐使用preset-201x之类的包,该包可以通过配置自动兼容代码,包括自动引入polyfill垫片处理新的API(例如:Promise
,Generator
,Symbol
等)以及 实例方法(例如Array.prototype.includes
等)。
使用
-
在根目录下创建.babelrc配置文件
{ "presets": [ [ "@babel/preset-env", { "targets": {}, "useBuiltIns": false, "corejs": false }] ] }
- targets: 针对项目指定生成适应环境的代码。如果不进行配置,babel会转义所有ES6+进行环境适配,十分不推荐该用法。
- useBuiltIns:该选项用来配合@babel/polyfill进行使用,针对
Babel > 7.4.0
, 官方不再推荐使用该库,请选择core-js
,根据安装core-js版本在corejs选项填写数字2或3。
-
安装
@babel/preset-env
以及core-js
源码
const test = () => { `es8`.padStart(2) }
转译结果
// useBuiltIns: false "use strict"; var test = function test() { 'es8'.padStart(2); };
// useBuiltIns: "usage" // corejs: 3 "use strict"; require("core-js/modules/es.string.pad-start"); var test = function test() { 'es8'.padStart(2); };
可以看出当使用
useBuiltIns: false
时, Babel只对箭头函数进行了转换,使用useBuiltIns: usage
时,Babel动态引入了core-js/modules/es.string.pad-start
为全局对象。
注: 我们知道,ES+中不仅包含新增的语法(如箭头函数、类),还有一些实例的扩展(Array.prototype.includes等),以及很多内置函数(如Promise、Symbol)。然而preset-env在不引入polyfill时,对于处理这些应用场景是无能为力的。而为了解决这样的问题,我们通常有两种方法:使用 Polyfill 或 Babel-runtime 进行功能填充。接下来我们会举例说明两者的优缺点以及应用场景。
@babel/polyfill
前文提到了一点 useBuiltIns
引入polyfill处理的简单例子,接下来我们再结合一些例子具体的了解polyfill的使用。
在 Babel > 7.4.0
之前,通常我们会安装 babel-polyfill
或 @babel/polyfill
来处理实例方法和ES+新增的内置函数,而7.4.0之后,当我们选择安装 @babel/polyfill
时,会收到如下的警告 :
warning @babel/polyfill@7.4.4: � As of Babel 7.4.0, this
package has been deprecated in favor of directly
including core-js/stable (to polyfill ECMAScript
features) and regenerator-runtime/runtime
(needed to use transpiled generator functions):
> import "core-js/stable";
> import "regenerator-runtime/runtime";
是不是有点懵逼ㄟ(▔,▔)ㄏ 。什么意思呢?其实polyfill本身就是stable版本的core-js和regenerator-runtime的合集,即 import @babel/polyfill
等同于:
import 'core-js/stable';
import 'regenerator-runtime/runtime';
所以在针对Babel >= 7.4.0
的情况,我们需要安装 core-js
替代 babel-polyfill
,而 regenerator-runtime
会在我们安装 @babel/runtime
时自动安装,所以不必单独安装了。
配合引入垫片polyfill的方式根据useBuiltIns
的不同可以分为三种,即 entry
, usage
和 false
。
源码
/******* useBuiltIns: entry 时添加一下两行代码 ********/
import 'core-js/stable';
import 'regenerator-runtime/runtime';
/****************************************************/
const a = new Promise();
let b = new Map()
转译结果
// 1. useBuiltIns: entry
"use strict";
require("core-js/modules/es.symbol");
require("core-js/modules/es.symbol.description");
require("core-js/modules/es.symbol.async-iterator");
// ..... 此处省略400个包
require("regenerator-runtime/runtime");
var a = new Promise();
var b = new Map();
// 2. useBuiltIns: usage
"use strict";
require("core-js/modules/es.array.iterator");
require("core-js/modules/es.map");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.promise");
require("core-js/modules/es.string.iterator");
require("core-js/modules/web.dom-collections.iterator");
var a = new Promise();
var b = new Map();
// 3. useBuiltIns: false
"use strict";
var a = new Promise();
var b = new Map();
对比三种使用方法我们可以发现,false
只做了语法转换, entry
引入了所有的es扩展包,不管用不用得着一股脑都打包进来,只有 usage
会自动检测代码中用到的功能自动引入模块(注:babel默认不会检测第三方依赖包代码,所以使用 usage
时,可能会出现引入第三方的代码包未注入模块而引发bug)。
所以,这里如果不考虑代码包大小,你可以选择 entry
方式。而如果你需要代码尽可能的精简,则使用 usage
,这也是官方推荐的用法。
思考: 到这里是不是觉得项目使用polyfill,写代码已经可以6的飞起了,哈哈~。那我们假设一种应用场景,假设我们需要发布一个类库给到别人使用,我们使用polyfill的方式引入了内置函数Promise,不巧的是别人的本地代码里也封装了一个函数叫Promise,完蛋,真李逵碰上了假李逵,你说你死不死 ̄□ ̄。 所以为了和平,这里就需要我们的
@babel/runtime
粉墨登场了。
@babel/runtime
babel-runtime一般应用于两种场景:
- 开发类库/工具(生成不污染全局空间和内置对象原型的代码)
- 借助
@babel/runtime
中帮助函数(helper function)移除冗余工具函数
注: 我们应该经常在其他文档和使用说明中看到
runtime
不支持实例方法,确实在之前的使用中,无论是Babel 7.0.0 ~ 7.4.0
抑或Babel < 7.0.0
, 对此都无能为力。他们只能通过配置corejs
(可选 false | 2)来决定是否使用babel/runtime-corejs
替代core-js
抑或polyfill
(可选 true | false)来决定是否全局引入新的内置函数(new built-ins),然而其实这两者并没有根本改变,corejs: 2
实际上等同于 7.0.0 版本之前的polyfill: true
。重点: 真正的改变出现在
Babel 7.4.0
之后,你可以选择引入@babel/runtime-corejs3
,设置corejs: 3
来帮助您实现对实例方法的支持。
我们接下来基于Babel 7.4.0
来看几个例子:
配置项
{
"presets": [
[
"@babel/preset-env"
],
],
"plugins": [
["@babel/plugin-transform-runtime", {
"corejs": false // 可选 false | 2 | 3
}]
]
}
源码 -- 移除冗余工具函数
class Person {}
转译结果
// 移除plugins中runtime配置
"use strict";
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Person = function Person() {
_classCallCheck(this, Person);
};
// 引入插件runtime
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
var Person = function Person() {
(0, _classCallCheck2["default"])(this, Person);
};
可以看出,引入runtime,可以避免在多文件时编译生成多次相同的工具函数。
源码 -全局污染和实例方法示例
const a = new Promise();
[1, 2, 3].includes(1)
转译结果
// corejs: false
"use strict";
var a = new Promise();
[1, 2, 3].includes(1);
// corejs: 2
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
var a = new _promise["default"]();
[1, 2, 3].includes(1);
// corejs: 3
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));
var _context;
var a = new _promise["default"]();
(0, _includes["default"])(_context = [1, 2, 3]).call(_context, 1);
结合一下代码我们可以看出,corejs: false
其实等同于使用 @babel/polyfill
时的 useBuiltIns: false
,只对ES语法进行了转换。corejs:2
等同于 Babel 6
时的 polyfill: true
,它们都会为代码创建一个沙盒环境,为 core-js
提供假名,这样就做到了不污染全局空间。corejs: 3
是在 corejs: 2
的基础上进而解决了之前无法实例方法的窘况,同时也保持了不污染全局空间,简直完美~
总结
Babel < 7.4.0
- 开发类库, 选择 @babel/runtime
- 内部项目,@babel/polyfill
Babel >= 7.4.0
,啥也不说,直接上@babel/runtime
吧,因为你要的全都有啊