跳至主内容

5.0.0 版本发布

· 1 分钟阅读
非官方测试版翻译

本页面由 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 测试版公告 中提到的类属性示例。

示例:

JavaScript
class Person {
firstName = "Sebastian";
static lastName = "McKenzie";
}

assert(new Person().firstName, "Sebastian");
assert(Person.lastName, "McKenzie");

使用方式:

JavaScript
require("babel").transform("code", {
optional: ["es7.classProperties"]
});
// or
require("babel").transform("code", { stage: 0 });
Shell
$ babel --optional es7.classProperties script.js
# or
$ babel --stage 0 script.js

Stage 1:装饰器

Yehuda Katz 提出的 stage 1 阶段 装饰器提案 支持优雅地组合属性描述符和元数据装饰。未来这将使强大的 Ember 对象模型 能通过原生类语法轻松实现。

示例:

JavaScript
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");

使用方式:

JavaScript
require("babel").transform("code", {
optional: ["es7.decorators"]
});
// or
require("babel").transform("code", { stage: 1 });
Shell
$ babel --optional es7.decorators script.js
# or
$ babel --stage 1 script.js

Stage 1:导出扩展

Lee Byron 提出的 stage 1 阶段 附加导出语句提案 完善了 import 和 export 语句的对称性,允许直接导出外部模块的命名空间和默认导出, 而无需修改当前作用域。

导出默认值

JavaScript
export foo from "bar";

等价于:

JavaScript
import _foo from "bar";
export { _foo as foo };

导出命名空间

JavaScript
export * as ns from "mod";

等价于:

JavaScript
import * as _ns from "mod";
export { _ns as ns };

使用方式:

JavaScript
require("babel").transform("code", {
optional: ["es7.exportExtensions"]
});
// or
require("babel").transform("code", { stage: 1 });
Shell
$ babel --optional es7.exportExtensions script.js
# or
$ babel --stage 1 script.js

React 性能优化

为迎接 React 0.14 版本,Babel 新增了针对 JSX 的优化转换器。

常量元素

从 React 0.14 开始,ReactElements 及其 props 对象可被视为值类型。 即当所有值相同时,不同实例在概念上是等价的。

以下面函数为例:

JavaScript
import React from "react";

function render() {
return <div className="foo" />;
}

可通过将 JSX 移出函数体进行优化,确保每次调用返回相同实例:

JavaScript
import React from "react";

var _ref = <div className="foo" />;

function render() {
return _ref;
}

这不仅支持对象复用,React 还会自动跳过常量组件的协调过程—— 无需手动实现 shouldComponentUpdate

使用方式:

JavaScript
require("babel").transform("code", {
optional: ["optimisation.react.constantElements"]
});
Shell
$ babel --optional optimisation.react.constantElements script.js

内联元素

仅限生产环境

内联元素功能应该only在生产环境启用, 因其会抑制多个 React 警告信息,在开发环境使用风险极高。

从 React 0.14 开始,ReactElements 可被内联为对象:

JavaScript
<div className="foo">{bar}<Baz key="baz" /></div>

等价形式:

JavaScript
{ type: 'div', props: { className: 'foo', children:
[ bar, { type: Baz, props: { }, key: 'baz', ref: null } ]
}, key: null, ref: null }

通过内联 React.createElement 的调用结果,显著提升性能。

使用方式:

JavaScript
require("babel").transform("code", {
optional: ["optimisation.react.inlineElements"]
});
Shell
$ babel --optional optimisation.react.inlineElements script.js

.babelrc 配置文件

Babel 5.0.0 默认支持在其所有集成中使用 .babelrc 文件。这意味着该功能可在 babel/registerbabel-node 以及各类构建系统插件和模块加载器(如 babel-loaderbabelify 等)中直接使用。

.babelrc 的作用等同于 JSHint 的 .jshintrc 和 JSCS 的 .jscsrc

JSON
{
"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()

JavaScript
class Foo extends Bar {
constructor() {
// no `super();`
}
}

在派生类的构造函数中,禁止super() 前访问 this

JavaScript
class Foo extends Bar {
constructor() {
this.foo; // `this` access before `super();`
super();
}
}

super() 仅限在派生类的构造函数中使用。

JavaScript
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 属性的情况😊),可能需要将这些操作提取到单独的文件中,并在需要它们的代码之前导入该文件。