这一章阐述如何配置及Babel6如何接入它自身的帮手方法和ES6标准库。
下面的Github仓库让你与我们在这里阐述的东西玩耍:babel-config-demo
通过Babel提供的代码通常有两种外部依赖:
- 助手方法(比如给子类用的)
- 标准库功能(比如Map或者ES6字符串方法)
实现这些依赖有两种方式:通过全局安装功能或通过模块。两种情况下的功能都有npm包交付。
下面的npm包全局安装它们变量并让你通过全局变量接入它:
- (P)
babel-plugin-external-helpers
和(I)生成的文件:全局帮手 - (I)
babel-polyfill
:全局标准库 - ES5,ES6+,运行时再生器
- (I)
core-js
:全局标准库 core-js/shim
:ES5,ES6+core-js/es6
:ES6
装配:
- (P)插件:以一个开发依赖安装的npm包并在Babel配置信息(参见“配置Babel6”章节)。
- (I)导入:以一个运行时依赖安装npm包并在开始编码时导入。
下面的npm包可通过模块用依赖:
- (P)
babel-plugin-transform-runtime
,(M)babel-runtime
:通过引入的帮手和标准库(插件生成各引入)。 - Babel帮手(必要)
- 标准库(可被关闭)
- 重生器API(可被关闭)
- (M)
core-js
:来自模块的标准库。 - 单体:
import _repeat from 'core-js/library/fn/string/repeat';
- 命名空间对象(ES5,ES6+):
import * as core from 'core-js/library';
const myStr = core.String.repeat('*', 10);
- 命名空间对象(ES6):
import * as core from 'core-js/library/es6';
const myStr = core.String.repeat('*', 10);
装配:
(P)插件:以一个开发依赖安装的npm包并在Babel配置信息(参见“配置Babel6”章节)。
(M)模块:以运行时依赖安装npm包并在运行时引入单体(当需要时)。
通过Babel生成的代码在两种额外的依赖中需要被实现。
第一种,大多数代码调用ES6标准库中功能。默认的,你通过全局变量接入这些功能:
let m = new Map();
if (str.startsWith('/')) ···
第二种,Babel的被代码翻译过的代码调用帮手方法(比如给子类用的)。默认的,帮手方法是行内的,插入每个文件。比如(_classCallCheck
是一个帮手方法):
//---------- 输入:ES6代码
class Person {}
//---------- 输出:用了帮手方法的ES5代码
"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);
};
一旦同样的帮手在多个文件中被使用各行内帮手会带到代码复制集中。
你能得到标准库和非行内帮手有其中的两种选择:通过全局变量和通过模块导入。如何去做会被下节中介绍。
有两个东西你需要下载:
- 插件
babel-plugin-external-helpers
: - 以开发环境依赖安装:
npm install --save-dev babel-plugin-external-helpers
- 在Babel配置信息中激活:
"plugins": ["external-helpers"]
- 设置上全局变量的
babelHelpers
文件: - 需要在运行时加载或引入,越早越好。举个例子:
import 'babelHelpers';
- 来自命令行工具
babel-external-helpers
(如何去做会被下节中介绍)生成的。 - 以一个开发依赖安装
babel-external-helpers
:
npm install --save-dev babel-cli
这插件确保所有帮手通过全局对象babelHelpers
的各方法调用。在这节,我将阐述它如何运作。在下一节,我会介绍如何假设babelHelpers
。
作为一个例子,考虑下面的ES6代码,在代码编译前:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
如果你不与external-helpers
而单用ES2016
前置项代码翻译它,你会得到:
"use strict";
var _createClass = (function () {
···
})();
function _classCallCheck(instance, Constructor) {
···
}
var Point = (function () {
function Point(x, y) {
_classCallCheck(this, Point);
this.x = x;
this.y = y;
}
_createClass(Point, [{
key: "toString",
value: function toString() {
return "(" + this.x + ", " + this.y + ")";
}
}]);
return Point;
})();
注意两个帮手函数_createClass
和_classCallCheck
。
如果你激活了external-helpers
插件,你会得到这个输出:
"use strict";
var Point = (function () {
function Point(x, y) {
babelHelpers.classCallCheck(this, Point);
this.x = x;
this.y = y;
}
babelHelpers.createClass(Point, [{
key: "toString",
value: function toString() {
return "(" + this.x + ", " + this.y + ")";
}
}]);
return Point;
})();
你如何有各帮手的对象到全局变量babelHelpers
中?通过一个有命令行工具babel-external-helpers
生成的文件。这个工具是npm包babel-cli
的一部分。这个文件适用三种格式:
babel-external-helpers -t global
显印一个置入各帮手至global.babelHelpers
的Node.js模块。
babel-external-helpers -t var
显印一个以var
声明至全局作用域并将一个有各帮手的对象转赋至它的babelHelpers
的脚本文件(浏览器代码)。
babel-external-helpers -t umd
显印一个以CommonJS模块,AMD模块及以一个脚本(通过全局变量)运作的普适模块定义(UMD)。
这个调用会显印有用的信息:
babel-external-helpers --help
babel-polyfill包含了一个会安装一些东西到全局变量里去的模块:
- ES5 polyfills(无论是否在ES5标准库里缺失):
Object.create()
,Array.prototype.forEach()
等等。 - ES6 polyfills:
Map
,String.prototype.repeat()
等等。 - 一些ECMAScript未来提案的polyfills:
Object.entries()
,Array.prototype.includes()
等。 - 在运行时用的重生器(那些由Babel去用来代码翻译ES6生成ES5的)。
- polyfills由
[core-js](https://github.com/zloirock/core-js)
提供,正如你所见当你看到两个导入表达式构成这个模块:
import "core-js/shim";
import "babel-regenerator-runtime";
当你发现任意以上功能在你的代码翻译的代码中缺失时通过npm以一个运行时的依赖安装babel-polyfill
。在当前的各Node.js版本(译者注:指7.0+版本),你大概不需要用到它,因为这些版本携带着大多数ES6标准库和本地化的生成器。
这个模块被通过这样安装:
npm install --save babel-polyfill
你必须在使用标准库之前引入它:
import 'babel-polyfill';
这个模块将检查全局变量并只会安装缺失的功能。这个polyfill的负面是你总是交付并加载全部的功能,无关你要用多少。
如果你不需要运行时的重生器,你可以直接使用core-js
polyfill。
你可以像这样安装它:
npm install --save
这有一些途径去安装polyfill功能。两个常见的是:
import 'core-js/shim';
ES5,ES6和ES6后的各功能Polyfills。
import 'core-js/es6';
仅ES6各功能的Polyfills。
import
表达式需要你接入运行时库才能生效。它全局地安装polyfills。
这个方法的两个部分:
- 一个包含帮手和标准库的模块。以一个运行时依赖安装:
npm install --save babel-runtime
- 一个改变Babel输出的插件以便帮手和标准库由
babel-runtime
被导入。 - 以一个开发依赖安装:
npm install --save-dev babel-plugin-transform-runtime
在Babel配置信息中激活:
"plugins": ["transform-runtime"]
这插件重定了三种来自babel-runtime
的导入操作:
- Babel各助手(必要)
- 标准库(可被关闭)
- 重生器API(可被关闭):通过
babel-plugin-transform-regenerator
使用来代码翻译ES6生成器至ES5代码。
babel-runtime
变为一个运行时依赖,但引入表达式由插件创建——你不需要引入任何东西。
这个插件可让你像以下方式关闭2号和3号:
"plugins": [
["transform-runtime",
{ "polyfill": false, "regenerator": false }],
]
下面两节探索transform-runtime
如何为帮手和标准库工作。
transform-runtime
对各帮手效果很好。考虑下面的ES6代码:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
有了插件,这会代码翻译成:
"use strict";
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); //(A)
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require("babel-runtime/helpers/createClass"); //(B)
var _createClass3 = _interopRequireDefault(_createClass2);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var Point = (function () {
function Point(x, y) {
(0, _classCallCheck3.default)(this, Point);
this.x = x;
this.y = y;
}
(0, _createClass3.default)(Point, [{
key: "toString",
value: function toString() {
return "(" + this.x + ", " + this.y + ")";
}
}]);
return Point;
})();
帮手的classCallCheck
(行A)及createClass
(行B)现在被babel-runtime
导入。要注意的是每一个方法坐落在他自己的模块中,确保你确实在用的仅有的各方法在一个打包的代码中结束。
帮手方法_interopRequireDefault
确保不仅朴实的CommonJS模块还有代码翻译过的ES6模块都能被使用。
ℹ️Babel如何编译ES6模块至CommonJS模块的细节会被“Babel和CommonJS模块”章介绍。
transform-runtime
插件自动处理帮手,但对于绝大多数的标准库,额外的工作是被需要的。让我们以下面的方式调试接入标准库的支持情况:
- 接入各函数(已支持)
- 常规地接入方法(未被支持)
- 通过定制各多功能函数接入方法(已支持)
transform-runtime
会做适当方法接入侦测:被命名空间的各函数(比如Object.assign
和Math.sign
)和各构造函数(比如Map
和Promise
)。比如,接下来的ES6代码:
let m = new Map();
Math.sign(-1);
This code is transpiled to:
"use strict";
var _sign = require("babel-runtime/core-js/math/sign"); //(A)
var _sign2 = _interopRequireDefault(_sign);
var _map = require("babel-runtime/core-js/map"); //(B)
var _map2 = _interopRequireDefault(_map);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var m = new _map2.default();
(0, _sign2.default)(-1);
注意行A和行B的各引入内容。
不过,transform-runtime
不会侦测像接下来那些ES6代码的方法调用:
console.log('a'.repeat(3));
console.log(String.prototype.repeat.call('b', 3));
这会被代码翻译成:
'use strict';
console.log('a'.repeat(3));
console.log(String.prototype.repeat.call('b', 3));
它们没有引入——输入的代码基本上是无法触及的。第一个方法调用是动态分发,所以transform-runtime
不能抓获它是不足为奇的。然而,第二个是直接方法调用并且一样会被忽略。
transform-runtime
为方法调用提供一个迂回空间——各多功能方法被附着到构造函数上。你能用它们去替换方法调用比如这个:
'c'.repeat(3);
用一个多功能函数接入:
String.repeat('c', 3);
被代码翻译的,代码类似这个(注意行A中的引入内容):
'use strict';
var _repeat = require('babel-runtime/core-js/string/repeat'); // (A)
var _repeat2 = _interopRequireDefault(_repeat);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
(0, _repeat2.default)('c', 3);
当你回过头看,所有通过transform-runtime
生成的引用从babel-runtime/core-js
开始使用模块ID。这是因为Babel的polyfilling是基于[core-js](https://github.com/zloirock/core-js)
库的。你能翻阅transform-runtime
如果详细遍历各定义并在仓库文件[definitions.js](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-runtime/src/definitions.js)
中链接各属性接入至core-js
模块。这是一个摘录:
module.exports = {
builtins: {
···
Set: "set",
···
},
methods: {
Array: {
···
from: "array/from",
···
},
···
String: {
···
repeat: "string/repeat",
···
},
···
}
};
那意味着transform-runtime
提供Set
,Array.from()
,String.repeat()
以及更多。
考虑到transform-runtime
当你想要调用一个方法需要你接入内置构造函数的属性,直接地用core-js
是对那个插件另一种有用的选择。
以一个运行时依赖安装它:
npm install --save core-js
非全局地接入标准库功能的一种途径是通过一个简单的库对象:
import * as core from 'core-js/library';
const mySet = new core.Set([1, 2, 3, 2, 1]);
const myArr = core.Array.from(mySet);
const myStr = core.String.repeat('*', 10);
至于库对象的内容,你在下面两者中有一个选择:
core-js/library
:ES5,ES6和少量提案特性的polyfillscore-js/library/es6
:仅ES6的polyfills
另一种非全局接入标准库的方式是独立得去引入各单体:
import _Set from 'core-js/library/fn/set';
import _from from 'core-js/library/fn/array/from';
import _repeat from 'core-js/library/fn/string/repeat';
const mySet = new _Set([1, 2, 3, 2, 1]);
const myArr = _from(mySet);
const myStr = _repeat('*', 10);
对于各库,你一定不要触碰全局变量,这是为什么一切都必须来自各引入:
- Babel各帮手:
babel-plugin-transform-runtime
和babel-runtime
很好用。 - 运行时重生器:前一个套餐也通过各引入提供重生器。如果你不用或者代码翻译重生器那么你能关闭
babel-plugin-transform-runtime
的这个部分。 标准库
:我不喜欢babel-plugin-transform-runtime
通过各非标准全局方法(比如Array.repeat())使ES6方法(比如Array.prototype.repeat()
)生效。我会将这个插件的这个部分关闭并直接使用core-js
。
对于各应用程序,你能使用混合途径:
- 使用Babel各帮手——如果有必要——通过模块导入的运行时重生器,正如对各库的阐述。
- 全局安装ES6标准库。而一个全局的polyfill将不作任何改变最终本地地运行得让你写代码。我发现那更重要。
考虑到重生器已经被照顾到了,你不需要babel-polyfill
;core-js
就足够了。
多余的各Babel帮手总归会只是一个优化,尤其是对于大基数的代码,因为对于节省空间很有价值。
致谢。感谢Denis Pushkarev(@zloirock)和Paul Klimashkin在这个内容上的反馈。
首页:架设ES6
上一章:配置Babel6
下一章:Babel的宽松模式