首页
学习
活动
专区
工具
TVP
发布

Babel 7.0正式发布,支持TypeScript,JSX Fragment

Babel 的重要意义

由于 JavaScript 在不断地发展,各种新标准和提案也就层出不穷,用户使用的浏览器也不同(尤其是旧版的 IE),这就导致了有些用户无法使用 JavaScript 的一些新特性,如果开发人员想使用新的语法(如 class A {}),在旧浏览器上就会因为这个问题出现 SyntaxError。

由于它能够转换 JavaScript 代码,还可用于实现新功能,因此它已成为 TC39 获取社区对 JavaScript 反馈的重要桥梁。

如今,Babel 是 JavaScript 开发的基础。它在 npm 上,每月有 1700 万次的下载量,用户包括主流框架(React,Vue,Ember,Polymer)的开发者和企业(Facebook,Netflix,Airbnb)。即使你自己没有使用它,你的依赖项也很可能正在使用 Babel。

Babel 由志愿者驱动

虽然 Babel 对 JavaScript 语言的未来,甚至对社区和 js 生态都产生了巨大的影响,但你肯定想不到,他们的维护者只是由社区的几个志愿者组成。

重大变更

  • 不再支持不在维护中的 Node 版本:0.10、0.12、4、5;
  • 使用 @babel 命名空间,因此 babel-core 就变成了 @babel/core;
  • 移除(并停止发布)任何年度预设(preset-es2015 等),@babel/preset-env 取代了对这些内容的需求 ;
  • 移除“Stage” 预设(@babel/preset-stage-0 等),同时移除了 @babel/polyfill 中的提议;
  • 重命名了某些包:任何 TC39 提议插件现在都是 -proposal 而不是 -transform,所以 @babel/plugin-transform-class-properties 变成了 @babel/plugin-proposal-class-properties;
  • 针对某些面向用户的包(例如 babel-loader、@babel/cli 等)在 @babel/core 中引入 peerDependency;

babel-upgrade

babel-upgrade 是一个用于进行自动升级的新工具:目前在 package.json 和.babelrc 配置文件中配置了依赖关系。

建议直接在 git 仓库上运行 npx babel-upgrade,或者使用 npm i babel-upgrade -g 进行全局安装。

如果你想修改文件,可以使用参数 --write 和 --install。

npx babel-upgrade --write --install

JavaScript 配置文件

Babel 7.0 引入了 babel.config.js。

*.js 配置文件在 JavaScript 生态系统中相当常见。ESLint 和 Webpack 分别使用了.eslintrc.js 和 webpack.config.js 配置文件。但这并不意味着它们就取代了.babelrc 或 package.json,这不是必要条件,但在某些情况下这可能很有用。以下是仅在“生产”环境中使用插件进行编译的配置。

babel.config.js 的配置解析方式与.babelrc 不同。它始终会解析该文件中的配置,而不会从每个文件向上查找,直到找到配置为止。这样就可以利用 overrides 特性。

var env = process.env.NODE_ENV;
module.exports = {
  plugins: [
    env === "production" && "babel-plugin-that-is-cool"
  ].filter(Boolean)
};

使用 overrides 进行选择性配置

module.exports = {
  presets: [
    // defeault config...
  ],
  overrides: [{
    test: ["./node_modules"],
    presets: [
      // config for node_modules
    ],
  }, {
    test: ["./tests"],
    presets: [
      // config for tests
    ],
  }]
};

有些应用程序需要针对测试、客户端代码和服务器代码使用不同的编译配置选项,通过这种方式就不需要在每个文件夹下创建.babelrc 文件了。

速度

Babel 团队做了很多变更来优化代码,并接受了来自 V8 团队的一些补丁(https://twitter.com/rauchg/status/924349334346276864)。

输出选项

Babel 支持预设和插件选项有一段时间了。比如,你可以将插件包装在一个数组中,并将选项对象传给插件:

// 减号表示移除,加号表示增加:
{
  "plugins": [
-   "pluginA",
+   ["pluginA", {
+     // options here
+   }],
  ]
}

Babel 团队还对部分插件的 loose 选项做了一些修改,并为部分插件添加了一些新选项!

比如,对于类 class A {},现在不包含 _classCallCheck。

class A {}
-------------------
// 减号表示移除:
var A = function A() {
-  _classCallCheck(this, A);
};

如果 for-of 循环的是一个数组,那么可以使用这个新选项:[“transform-for-of”,{“assumeArray”:true}]

let elm;
for (elm of array) {
  console.log(elm);
}
----------------------
let elm;
for (let _i = 0, _array = array; _i < _array.length; _i++) {
  elm = _array[_i];
  console.log(elm);
}

在 loose 模式下将 transform-typeof-symbol 插件排除在外。

“纯粹”的注解支持

转换后的 ES6 类使用/*#__PURE__*/进行注解,这样就可以告诉 Uglify 和 babel-minify 移除死代码。这些注解也被添加到其他辅助函数中。

class C {
  m() {}
}
---------------------
var C =
/*#__PURE__*/
function () {
  // ...
}();

TC39 提案支持

以下是 Babel 支持的一些新语法以及已经添加到 v7 中的语法清单:

  • ES2018:Object Rest Spread (var a = { b, ...c })
  • ES2018(新):Unicode 属性正则表达式
  • ES2018(新):JSON 超集
  • ES2015(新):new.target
  • 阶段 3(新):类私有实例字段(class A { #b = 2 })
  • 阶段 3(WIP):静态类字段、私有静态方法(class A { static #a() {} })
  • 阶段 3(新):可选 Catch 绑定 try { throw 0 } catch { do() }
  • 第 3 阶段(新):BigInt(仅限语法)
  • 第 3 阶段:动态导入(import("a"))
  • 第 2 阶段(新):import.meta(仅限语法)(import.meta.url)
  • 第 2 阶段(新):数字分隔器(1_000)
  • 第 2 阶段(新):function.sent
  • 第 2 阶段:export-namespace-from(export * as ns from'mod'),从 export-extensions 中拆分出来
  • 第 2 阶段:装饰器
  • 第 1 阶段:export-default-from(export v from 'mod'),从 export-extensions 中拆分出来
  • 第 1 阶段(新):可选链接(a?.b)
  • 第 1 阶段(新):逻辑赋值(a &&= b; a ||= b)
  • 第 1 阶段(新):Nullish Coalescing(a ?? b)
  • 第 1 阶段(新):管道操作符(a |> b)
  • 第 1 阶段(新):throw 表达式(() => throw new Error("a"))

TypeScript 支持

Babel 团队与 TypeScript 团队合作,让 Babel 使用 @babel/preset-typescript 解析 / 转换类型语法,类似于使用 @babel/preset-flow 处理 Flow 的方式。

之前(有类型):

interface Person {
  firstName: string;
  lastName: string;
}

function greeter(person : Person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}

之后(移除了类型):

function greeter(person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}

JSX Fragment 支持(<>)

正如 React 博客中所提到的,从 Beta.31 开始,已经支持 JSX Fragment。

render() {
  return (
    <>
      <ChildA />
      <ChildB />
    </>
  );
}
-----------------------
// output
render() {
  return React.createElement(
    React.Fragment,
    null,
    React.createElement(ChildA, null),
    React.createElement(ChildB, null)
  );
}

Babel 辅助函数的变化

@babel/runtime 已经被分为 @babel/runtime 和 @babel/runtime-corejs2。前者仅包含 Babel 的辅助函数,后者包含辅助函数以及 polyfill 函数(例如 Symbol、Promise)。

规范规定了需要通过 new Person() 实例化一个类,但如果它被编译成一个函数,就可以直接调用 Person(),所以他们提供了一个运行时检查。

class Person {}
------------------------
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-transform-runtime 和 @babel/runtime(作为依赖项),Babel 可以提取单个函数,而且只需要模块函数来获得较小的输出,如下所示:

var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");

var Person = function Person() {
  _classCallCheck(this, Person);
};

自动 polyfill(实验性质)

要使用 polyfill,只需导入 @babel/polyfill 即可:

import "@babel/polyfill";

但这样会导入整个 polyfill,如果浏览器已经支持某些特性,就不需要导入所有内容。可以使用选项 useBuiltins:”entry”来导入需要用到的部分。

可以更进一步,通过选项 useBuiltIns:”usage”只将需要用到的 polyfill 导入到代码库中。

它将遍历每个文件,并在每个使用了内置 polyfill 的文件的顶部注入一个导入语句。例如:

import "core-js/modules/es6.promise";
var a = new Promise();

但它的推理不是很完美,可能会出现误报:

import "core-js/modules/es7.array.includes";
a.includes // assume a is an []

Babel 宏

Babel 的一大特点是它的可插拔性。多年来,Babel 从一个“6to5”编译器转变为代码转换平台,为用户和开发人员提供了出色的优化选项。人们已经为特定库和用例开发了大量的 Babel 插件,用以提升库 API 的性能和功能。

可惜的是,将这些插件添加到代码库中需要更改配置,从而增加了代码复杂性。由 Kent C. Dodds 开发的 babel-plugin-macros(https://github.com/kentcdodds/babel-plugin-macros)已经解决了这个!

在安装了 babel-plugin-macros 并将其添加到配置中(它已包含在 create-react-app v2 中)之后,你就不必费心配置就可以使用宏了。此外,为特定应用程序或代码编写自定义转换也变得更加容易。

模块目标

Babel 一直试图在转换的影响范围和功能之间做出平衡。在 Babel 7 中,通过配置 Babel 来支持模块 / 非模块模式变得更加容易。

值得注意的是,一些流行的 Web 框架的 CLI 工具已经在利用这些支持,这让转换后的 JavaScript 代码减少了大约 20%。

class C extends HTMLElement {}

Babel 总是会警告说它不支持扩展原生内置元素(Array、Error 等),但现在可以了!Babel 团队对类插件进行了修改,如果你使用了 preset-env,那么默认就自动启用了这个特性。

网站变更

Babel 网站从 Jekyll(https://jekyllrb.com/)搬到了 Docusaurus(https://docusaurus.io/)。

REPL

Babel 将 REPL 重写为 React 组件,并更好地与 CodeSandbox 集成。这样你就可以在 REPL 中安装来自 npm 的任何插件或预设,并获取 CodeSandbox 的任何更新。

接下来要做什么

Babel 本质上就是要与 JavaScript 紧紧联系在一起,包括在语法变得“稳定”之前花时间和精力来实现和维护语法。Babel 团队关心的是整个过程:升级路径、新特性的传播、标准 / 语言设计的教学、易用性以及与其他项目的集成。

在 Nicolò的帮助下,Babel 团队几乎完成了新的装饰器的实现。这是一个漫长的旅程,因为新的提案完全不同,而且比旧的更强大。它可能会在下一个次要版本中发布。

还有很多新特性还在开发当中:插件顺序、更好的验证 / 错误、速度提升、新的 loose/spec 选项、缓存、异步使用 Babel、从 CI 构建、冒烟测试、运行 test262。

查看英文原文:https://babeljs.io/blog/2018/08/27/7.0.0

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址http://www.infoq.com/cn/news/2018/08/babel7-published
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券