1.前言
Babel 的 loose 模式将 ES6 代码转译成 ES5 代码,loose 模式是不太忠实于 ES6 语义的。这篇文章解释了它是怎么工作的以及它的优点与缺点(剧透:通常是不推荐的)。
在这之前,我们先简单了解一下 Babel 中的一些基础知识:
- 配置文件
Babel 的配置文件是
.babelrc
,存放在下项目的根目录下,该文件用来设置预设和插件,基本格式如下: { "presets":[], "plugins":[] } - presets(预设)
为了将 Babel 的输出配置为符合要求的情况,我们需要指定 Babel 使用什么插件。可以指定的有:
- 单独的插件
- 预设,即支持各种编译方案的插件集合。
下面是一些使用广泛的预设:
- es2015:将 ES6 代码编译为 ES5
- stage-3:将
stage 3 ECMAScript proposals
编译为ES5 - react:将 JSX 编译为 JavaScript 并且移除 Flow 的类型注解
预设通过 npm 安装。他们在 npm 中的包名称为其名字加上前缀
babel-preset-
。比如安装es2015
时,我们可以用以下的命令: npm install babel-preset-es2015 - plugins(插件)
笼统地讲,插件是在编译过程中应用到输入中的函数。插件有两种重要的类别:
- 语法插件。其作用为使 Babel 具备处理内建基础语法之外的语法实体。它们能够帮助构造抽象语法树(AST)。典型的语法插件有:syntax-async-functions 以及 syntax-jsx。
- 转换插件。其作用为修改抽象语法树。典型的转换插件有:transform-async-to-generator、transform-react-jsx、transform-es2015-arrow-functions 等。
如果你想编译不包含在基础语法之内的代码,那你将同时需要一个语法插件以及与之相对应的转换插件。不过,每个依赖于语法插件的转换插件都将自动触发该语法插件。
插件同样通过 npm 安装,他们在 npm 中的包名称为其名字加上前缀
babel-plugin-
:- 安装插件
syntax-jsx
:npm install babel-plugin-syntax-jsx
- 安装插件
transform-react-jsx
:npm install babel-plugin-transform-react-jsx
- 安装插件
OK,基础知识介绍到这里,如想进一步学习 Babel,可以去到Babel官网。下面回归主题,探究 Babel 6 的 loose 模式。
2. 两种模式
许多 Babel 的插件有两种模式:
• 尽可能符合 ECMAScript6 语义的 normal 模式。
• 提供更简单 ES5 代码的 loose 模式。
通常,推荐不使用 loose 模式,使用这种模式的优点和缺点是:
• 优点:生成的代码可能更快,对老的引擎有更好的兼容性,代码通常更简洁,更加的“ES5化”。
• 缺点:你是在冒险——随后从转译的 ES6 到原生的 ES6 时你会遇到问题。这个险是很不值得冒的。
2.1 切换到 loose 模式
es2015-loose 是标准的 ES6 预设(preset)es2015 的 loose 版。这个预设的代码对什么样的插件有 loose 模式以及怎样开启提供了很好的说明。以下是代码截选:
代码语言:javascript复制module.exports = {
plugins: [
···
[require("babel-plugin-transform-es2015-classes"), {loose: true}],
require("babel-plugin-transform-es2015-object-super"),
···
]
};
这是一个 CommonJS 模块,可以使用任何的 ECMAScript 5,如果你通过.babelrc
或者package.json
配置 babel(详细配置),你需要使用 JSON。也可以包含 preset:
···
"presets": [
···
"es2015-loose",
···
],
···
也可以单独引入插件:
代码语言:javascript复制···
"plugins": [
···
["transform-es2015-classes", {loose: true}],
"transform-es2015-object-super",
···
],
···
3. 例子:normal 模式与 loose 模式的输出
让我们看看模式如何影响下面代码的编译:
代码语言:javascript复制class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
3.1 normal 模式
在 normal 模式下,类的 prototype 方法是通过Object.defineProperty
添加的(第 A 行),来确保它们是不可以被枚举的,这是 ES6 规范所要求的。
"use strict";
var _createClass = (function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i ) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor); // (A)
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
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;
})();
3.2 loose 模式
在 loose 模式下,用通常的赋值方式添加方法(第 B 行),这种风格更像你用 ES5 手动编写代码。
代码语言:javascript复制 "use strict";
function _classCallCheck(instance, Constructor) { ··· }
var Point = (function () {
function Point(x, y) {
_classCallCheck(this, Point);
this.x = x;
this.y = y;
}
Point.prototype.toString = function toString() { // (B)
return "(" this.x ", " this.y ")";
};
return Point;
})();
参考文献:
wd4blue-Babel 6: loose 模式 2ality-Configuring Babel