5.0.0 发布的详细指南

122 阅读6分钟

5.0.0版发布

在过去的几个月里,Babel受到了几个主要社区的欢迎,如Node、React、Ember、Backbone、Angular、Rails和其他许多社区。我们在几周前才推出了用户页面,看到每个人都在使用它,这真的很酷。像CloudFlare、Netflix、Mozilla和Yahoo!等公司。像Ghost、Atom、Mapbox等项目,还有很多。

我们已经看到了大量关于使用Babel的ES6+的博客文章、讲座、活动和课程,官方的Babel工具已经被下载了近200万次。

今天,我们正在制作迄今为止最大的Babel版本。

如果您是从Babel 4.x升级过来的,请看突发变化

整个内部遍历和转换管道经历了一次重写,大大增加了灵活性,并将允许许多未来管道的性能优化。

这个版本还带来了一个插件API,这使得消费者可以插入他们自己的自定义转化器,以利用Babel所提供的强大的转化机制。

你可以在这里查看完整的变化日志。

像往常一样**,如果你遇到任何回归问题,请立即报告**。

TC39进程

在这个版本中,你会看到我们开始与TC39进程保持一致。TC39是ECMA的一个技术委员会,负责编写ECMAScript标准。他们的流程分为5个阶段。

  • 第0阶段 - 草稿
  • 第1阶段 - 提案
  • 第二阶段 - 草案
  • 第三阶段 - 候选
  • 第四阶段 - 完成

第二阶段或以上的提案在Babel中是默认启用的。但这并不意味着它们一定会被纳入未来的ECMAScript规范,甚至是Babel本身。第二阶段被认为是默认纳入Babel的好时机,因为它们相对成熟,需要关键的提案反馈。

现在让我们深入了解我们对5.0所做的修改。


新功能

新建议

第0阶段:类属性

Jeff Morrison的第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

第一阶段装饰器

Yehuda Katz的第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

阶段1:导出扩展

Lee Byron的第1阶段额外的export-from语句提案完成了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的一些优化转换器。

常量元素

从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

内联元素

仅限生产

Inline Elements应该在生产中启用,因为多个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。这使你能够钩住Babel强大的遍历和转换内部。更多信息请参见文档

破坏性变化

实验性选项

experimental 选项已被删除。不过不用担心,有一个替代品。Babel现在按TC39阶段对ES7变压器进行分类。

tl;dr 如果你正在使用experimental 选项,只需将其改为$ babel --stage 0{ stage: 0 }

提醒大家。 阶段2或以上的提案是默认启用的。

阶段0

  • es7.classProperties
  • es7.comprehensions

第一阶段

  • es7.asyncFunctions
  • es7.decorators
  • es7.exportExtensions
  • es7.objectRestSpread

2阶段(第2阶段及以上的提案是默认启用的)

  • es7.exponentiationOperator

关于所有当前ES7提案的列表,请参见tc39/ecma262 repo

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
  }
}

删除的功能

  • 游乐场已经被移除,因此开发可以集中在主流的ES功能和建议上。这也降低了语法冲突的风险,使某些官方特性无法实现。
  • 抽象引用已被删除,因为该提议已被取代。对一个或多个被取代的提案的支持可能会在未来实施。

最后,我们希望你现在和我们一样对这个版本感到兴奋。这里面有很多东西,我们相信这将为我们在未来很长一段时间内的发展奠定基础。

- 巴别尔团队

进口现在被吊起来了

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 属性:)--你可能想把它提取到自己的文件中,并在需要它的代码之前导入。