5.0.0 版本发布
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
过去几个月,Babel 已被 Node、React、Ember、Backbone、Angular、Rails 等众多主流社区广泛采用。我们几周前刚上线了用户页面,看到如此多的用户使用令人振奋。包括 CloudFlare、Netflix、Mozilla 和 Yahoo! 等企业,以及 Ghost、Atom、Mapbox 等项目都在使用。
我们看到了大量关于 ES6+ 和 Babel 的博客文章、演讲、活动和课程,官方 Babel 工具下载量已接近 200 万次。
今天我们发布了 Babel 有史以来最重要的版本更新。
若您正在从 Babel 4.x 升级,请务必查阅破坏性变更说明。
本次发布包含以下 ES7 新提案:
我们完全重写了内部的遍历和转换管道架构,显著提升了灵活性,并为未来的管道性能优化铺平了道路。
本次更新还引入了插件 API,开发者可接入自定义转换器,充分利用 Babel 强大的转换机制。
完整更新日志请参见此处。
如遇任何回归问题,请立即报告。
TC39 标准化流程
本次版本开始,我们将遵循 TC39 标准化流程。TC39 是 ECMA 负责制定 ECMAScript 标准的技术委员会,其标准化流程分为五个阶段:
-
Stage 0 - 提案雏形
-
Stage 1 - 正式提案
-
Stage 2 - 草案阶段
-
Stage 3 - 候选阶段
-
Stage 4 - 完成阶段
Stage 2 及以上的提案将在 Babel 中默认启用。但这不意味着它们一定会被纳入未来 ECMAScript 规范或 Babel 本身。Stage 2 提案因相对成熟且需要关键性反馈,是纳入 Babel 的合理节点。
现在让我们深入解析 5.0 版本的变更内容。
目录:
New Features
新提案支持
Stage 0:类属性
Jeff Morrison 提出的 stage 0 阶段 类属性初始化提案 填补了类属性组合的空白。该特性等同于 React 0.13 测试版公告 中提到的类属性示例。
示例:
class Person {
firstName = "Sebastian";
static lastName = "McKenzie";
}
assert(new Person().firstName, "Sebastian");
assert(Person.lastName, "McKenzie");
使用方式:
require("babel").transform("code", {
optional: ["es7.classProperties"]
});
// or
require("babel").transform("code", { stage: 0 });
$ babel --optional es7.classProperties script.js
# or
$ babel --stage 0 script.js
Stage 1:装饰器
Yehuda Katz 提出的 stage 1 阶段 装饰器提案 支持优雅地组合属性描述符和元数据装饰。未来这将使强大的 Ember 对象模型 能通过原生类语法轻松实现。
示例:
function concat(...args) {
let sep = args.pop();
return function(target, key, descriptor) {
descriptor.initializer = function() {
return args.map(arg => this[arg]).join(sep);
}
}
}
function autobind(target, key, descriptor) {
var fn = descriptor.value;
delete descriptor.value;
delete descriptor.writable;
descriptor.get = function () {
var bound = fn.bind(this);
Object.defineProperty(this, key, {
configurable: true,
writable: true,
value: bound
});
return bound;
};
}
class Person {
firstName = "Sebastian";
lastName = "McKenzie";
@concat("firstName", "lastName", " ") fullName;
@concat("lastName", "firstName", ", ") formalName;
@autobind
getFullName() {
return `${this.firstName} ${this.lastName}`;
}
}
assert(new Person().fullName, "Sebastian McKenzie");
assert(new Person().formalName, "McKenzie, Sebastian");
assert(new Person().getFullName.call(null), "Sebastian McKenzie");
使用方式:
require("babel").transform("code", {
optional: ["es7.decorators"]
});
// or
require("babel").transform("code", { stage: 1 });
$ babel --optional es7.decorators script.js
# or
$ babel --stage 1 script.js
Stage 1:导出扩展
Lee Byron 提出的 stage 1 阶段 附加导出语句提案 完善了 import 和 export 语句的对称性,允许直接导出外部模块的命名空间和默认导出, 而无需修改当前作用域。
导出默认值
export foo from "bar";
等价于:
import _foo from "bar";
export { _foo as foo };
导出命名空间
export * as ns from "mod";
等价于:
import * as _ns from "mod";
export { _ns as ns };
使用方式:
require("babel").transform("code", {
optional: ["es7.exportExtensions"]
});
// or
require("babel").transform("code", { stage: 1 });
$ babel --optional es7.exportExtensions script.js
# or
$ babel --stage 1 script.js
React 性能优化
为迎接 React 0.14 版本,Babel 新增了针对 JSX 的优化转换器。
常量元素
从 React 0.14 开始,ReactElements 及其 props 对象可被视为值类型。 即当所有值相同时,不同实例在概念上是等价的。
以下面函数为例:
import React from "react";
function render() {
return <div className="foo" />;
}
可通过将 JSX 移出函数体进行优化,确保每次调用返回相同实例:
import React from "react";
var _ref = <div className="foo" />;
function render() {
return _ref;
}
这不仅支持对象复用,React 还会自动跳过常量组件的协调过程——
无需手动实现 shouldComponentUpdate。
使用方式:
require("babel").transform("code", {
optional: ["optimisation.react.constantElements"]
});
$ babel --optional optimisation.react.constantElements script.js
内联元素
内联元素功能应该only在生产环境启用, 因其会抑制多个 React 警告信息,在开发环境使用风险极高。
从 React 0.14 开始,ReactElements 可被内联为对象:
<div className="foo">{bar}<Baz key="baz" /></div>
等价形式:
{ type: 'div', props: { className: 'foo', children:
[ bar, { type: Baz, props: { }, key: 'baz', ref: null } ]
}, key: null, ref: null }
通过内联 React.createElement 的调用结果,显著提升性能。
使用方式:
require("babel").transform("code", {
optional: ["optimisation.react.inlineElements"]
});
$ babel --optional optimisation.react.inlineElements script.js
.babelrc 配置文件
Babel 5.0.0 默认支持在其所有集成中使用 .babelrc 文件。这意味着该功能可在 babel/register、babel-node 以及各类构建系统插件和模块加载器(如 babel-loader 和 babelify 等)中直接使用。
.babelrc 的作用等同于 JSHint 的 .jshintrc 和 JSCS 的 .jscsrc。
{
"stage": 1,
"ignore": [
"foo.js",
"bar/**/*.js"
]
}
更多信息请参阅文档。
插件 API
5.0.0 版本还引入了期待已久的插件 API。通过该 API,您可以接入 Babel 强大的内部遍历和转换机制。详情请参阅文档。
Breaking Changes
实验性选项变更
experimental 选项已被移除。但无需担心,已有替代方案:Babel 现在根据 TC39 阶段对 ES7 转换器进行分类。
简而言之:若您正在使用 experimental 选项,只需将其替换为 $ babel --stage 0 或 { stage: 0 }。
提醒: 阶段 2 及以上的提案默认启用。
阶段 0
-
es7.classProperties -
es7.comprehensions
阶段 1
-
es7.asyncFunctions -
es7.decorators -
es7.exportExtensions -
es7.objectRestSpread
阶段 2(阶段 2 及以上的提案默认启用)
es7.exponentiationOperator
当前所有 ES7 提案列表请参见 tc39/ecma262 仓库。
returnUsedHelpers 选项变更
returnUsedHelpers 选项已更名为 metadataUsedHelpers,其返回结果对象也从 usedHelpers 调整为 metadata.usedHelpers。
类相关变更
5.0.0 引入了一些早该更新的派生类语义规则。
在派生类的构造函数中必须调用 super()。
class Foo extends Bar {
constructor() {
// no `super();`
}
}
在派生类的构造函数中,禁止在 super() 前访问 this。
class Foo extends Bar {
constructor() {
this.foo; // `this` access before `super();`
super();
}
}
super() 仅限在派生类的构造函数中使用。
class Foo {
constructor() {
super(); // not in a derived constructor
}
}
移除的功能
-
移除了 Playground,以便集中开发主流 ES 特性和提案。这也降低了语法冲突导致官方特性无法实现的风险。
-
移除了抽象引用,因该提案已被取代。未来可能支持取代该提案的一个或多个新方案。
最后,希望您和我们一样对此次发布感到兴奋。我们为此付出了巨大努力,相信这将为未来发展奠定坚实基础。
— The Babel team
导入语句现在会被提升
在 4.x 版本中,导入语句会按其在代码中的位置内联处理。这意味着以下代码:
global.test = 'test'
import './test'
会被编译为:
'use strict';
global.test = 'test';
require('./test');
然而,从 5.x 版本开始,此行为已更改为遵循 ES6 规范,导入语句将现在被提升。在实际代码中,这意味着上述代码片段将被转换为类似以下形式:
'use strict';
require('./test');
global.test = 'test';
如果你的代码需要在特定模块导入之间执行某些操作(例如测试代码时需要模拟某些 window 属性的情况😊),可能需要将这些操作提取到单独的文件中,并在需要它们的代码之前导入该文件。