跳至主内容

升级到 Babel 7

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

升级到 Babel 7 时请参考本文档。查看此处了解 API/集成变更。

由于并非每个破坏性变更都会影响所有项目,我们已按升级时导致测试失败的可能性对章节进行了排序。

整个 Babel

已停止支持 Node.js 0.10、0.12、4 和 5 版本 #5025, #5041, #7755, #5186

我们强烈建议使用较新的 Node.js 版本(LTS v8),因为旧版本已不再维护。 更多信息请参阅 nodejs/LTS

这仅意味着 Babel _本身_无法在旧版本的 Node.js 上运行,但它仍然可以_输出_能在旧 Node.js 版本上运行的代码。

配置查找方式变更

更多信息请阅读我们的 6.x 与 7.x 对比文档

Babel 先前在处理 node_modules、符号链接和单体仓库时存在问题。我们已对此进行改进:Babel 将在 package.json 边界停止向上查找。针对单体仓库,我们新增了 babel.config.js 文件来集中管理所有包的配置(也可选择为每个包单独配置)。在 7.1 版本中,我们引入了 rootMode 选项以便在必要时进行更深层查找。

年度预设弃用

"env" 预设已推出超过一年,它完全取代了我们先前使用和推荐的部分预设。

  • babel-preset-es2015

  • babel-preset-es2016

  • babel-preset-es2017

  • babel-preset-latest

  • 上述预设的组合 ^

这些预设应替换为 "env" 预设。

阶段预设弃用

我们正在弃用阶段预设,改为显式使用具体提案。更多迁移步骤请查看 stage-0 README

可通过运行 npx babel-upgrade 自动完成此操作(相关 PR 见此处)。

移除 @babel/polyfill 中的提案垫片

基于相同考量,我们已从 @babel/polyfill 中移除提案垫片。

目前 @babel/polyfill 实质上是 core-js v2 的别名。源码

此前它仅包含两个导入:

JavaScript
import "core-js/shim"; // included < Stage 4 proposals
import "regenerator-runtime/runtime";

如需使用提案特性,需单独导入相关 polyfill。建议直接从 core-js 包或 npm 上的其他包导入。

例如:

JavaScript
// for core-js v2:
import "core-js/fn/array/flat-map";

// for core-js v3:
import "core-js/features/array/flat-map";

以下是 core-js v2 中 Stage < 3 提案的 polyfill 列表:

Details
JavaScript
// core-js v2

// Stage 3
import "core-js/fn/string/trim-left";
import "core-js/fn/string/trim-right";
import "core-js/fn/string/match-all";
import "core-js/fn/array/flat-map";
import "core-js/fn/array/flatten"; // RENAMED
import "core-js/fn/global";

// Stage 1
import "core-js/fn/symbol/observable";
import "core-js/fn/promise/try";
import "core-js/fn/observable";

// Stage 1 Math Extensions
import "core-js/fn/math/clamp";
import "core-js/fn/math/deg-per-rad";
import "core-js/fn/math/degrees";
import "core-js/fn/math/fscale";
import "core-js/fn/math/iaddh";
import "core-js/fn/math/isubh";
import "core-js/fn/math/imulh";
import "core-js/fn/math/rad-per-deg";
import "core-js/fn/math/radians";
import "core-js/fn/math/scale";
import "core-js/fn/math/umulh";
import "core-js/fn/math/signbit";

// Stage 1 "of and from on collection constructors"
import "core-js/fn/map/of";
import "core-js/fn/set/of";
import "core-js/fn/weak-map/of";
import "core-js/fn/weak-set/of";
import "core-js/fn/map/from";
import "core-js/fn/set/from";
import "core-js/fn/weak-map/from";
import "core-js/fn/weak-set/from";

// Stage 0
import "core-js/fn/string/at";

// Nonstandard
import "core-js/fn/object/define-getter";
import "core-js/fn/object/define-setter";
import "core-js/fn/object/lookup-getter";
import "core-js/fn/object/lookup-setter";
// import "core-js/fn/map/to-json"; // Not available standalone
// import "core-js/fn/set/to-json"; // Not available standalone

import "core-js/fn/system/global";
import "core-js/fn/error/is-error";
import "core-js/fn/asap";

// Decorator metadata? Not sure of stage/proposal
import "core-js/fn/reflect/define-metadata";
import "core-js/fn/reflect/delete-metadata";
import "core-js/fn/reflect/get-metadata";
import "core-js/fn/reflect/get-metadata-keys";
import "core-js/fn/reflect/get-own-metadata";
import "core-js/fn/reflect/get-own-metadata-keys";
import "core-js/fn/reflect/has-metadata";
import "core-js/fn/reflect/has-own-metadata";
import "core-js/fn/reflect/metadata";

版本控制/依赖关系

多数插件/顶层包现在都将 @babel/core 设为 peerDependency

包重命名

  • babylon 已更名为 @babel/parser

配置中仍可使用包名的简写形式(省略 preset-plugin- 前缀),但为清晰起见,建议使用完整包名(考虑到简写节省的输入量有限,或许应直接弃用该方式)。

{
- "presets": ["@babel/preset-react"],
+ "presets": ["@babel/react"], // this is equivalent
- "plugins": ["@babel/transform-runtime"],
+ "plugins": ["@babel/plugin-transform-runtime"], // same
}

作用域包

最重要的变更是将所有包改为作用域包monorepo 中的文件夹名称不变,但 package.json 中的名称已更改)。

此举彻底解决了名称被意外/恶意占用的问题,实现了与社区插件的明确分离,并简化了命名规范。

依赖项需按如下方式修改:

babel-cli@babel/cli。本质上就是将 babel- 前缀替换为 @babel/

配置中的使用

仍可使用预设或插件的简写形式。但由于作用域包的变更,必须保留 @babel/ 前缀,如同配置自定义预设时需指定作用域。

babel.config.js
module.exports = {
presets: ["@babel/env"], // "@babel/preset-env"
plugins: ["@babel/transform-arrow-functions"], // same as "@babel/plugin-transform-arrow-functions"
};

为 TC39 提案统一使用 -proposal- 前缀

未纳入年度版本(ES2015、ES2016 等)的插件均需重命名为 -proposal 前缀,以明确标识该特性尚未正式成为 JavaScript 标准。

例如:

  • @babel/plugin-transform-function-bind@babel/plugin-proposal-function-bind(Stage 0)

  • @babel/plugin-transform-class-properties@babel/plugin-proposal-class-properties(Stage 3)

这也意味着当提案进入 Stage 4 阶段时,需再次重命名插件包。

移除包名中的年份标识

部分插件名称中的 -es3--es2015- 等年份标识已被移除(因其必要性不足)。

@babel/plugin-transform-es2015-classes@babel/plugin-transform-classes

CommonJS 中的 "use strict"this

Babel 6 的 ES6 模块转换会无条件处理所有指定文件,无论文件是否包含 ES6 导入/导出语句。这导致所有被处理的 CommonJS 模块中,文件作用域的 this 被重写为 undefined,且均在顶部插入了 "use strict"

JavaScript
// input.js
this;
JavaScript
// output.js v6
"use strict"; // assumed strict modules
undefined; // changed this to undefined
JavaScript
// output.js v7
this;

在 Babel 7 中,此行为已被限制:对于 transform-es2015-modules-commonjs 转换,仅当文件中包含 ES6 导入或导出时才会修改文件。(编者注:如果我们将 https://github.com/babel/babel/issues/6242 落地,这可能会再次更改,因此在发布前需要重新审视。)

JavaScript
// input2.js
import "a";
JavaScript
// output.js v6 and v7
"use strict";
require("a");

如果你之前依赖 Babel 自动在所有 CommonJS 模块中注入 "use strict",那么现在你需要在 Babel 配置中显式使用 transform-strict-mode 插件。

React 和 Flow 预设的分离

babel-preset-react 一直包含 flow 插件。这导致了很多问题:用户可能因拼写错误而意外使用了 flow 语法,或在没有使用 flow 本身进行类型检查的情况下添加了它,从而导致错误。

当我们决定支持 TypeScript 时,这个问题变得更加复杂。如果你想同时使用 React 和 TypeScript 预设,我们必须找到一种方法,通过文件类型或指令自动开启/关闭语法。最终,完全分离预设更为简单。

预设使 Babel 能够解析 Flow / TypeScript(以及其他方言/语言)提供的类型,然后在编译为 JavaScript 时将其去除。

{
- "presets": ["@babel/preset-react"]
+ "presets": ["@babel/preset-react", "@babel/preset-flow"] // parse & remove flow types
+ "presets": ["@babel/preset-react", "@babel/preset-typescript"] // parse & remove typescript types
}

选项解析

Babel 的配置选项比 Babel 6 更加严格。以前,使用逗号分隔的预设列表(例如 "presets": 'es2015, es2016')在技术上可行,但现在会失败,需要改为数组形式 #5463

注意:这不适用于 CLI,在 CLI 中 --presets es2015,es2016 仍然有效。

{
- "presets": "@babel/preset-env, @babel/preset-react"
+ "presets": ["@babel/preset-env", "@babel/preset-react"]
}

插件/预设的导出

为了保持一致性,所有插件/预设现在应该导出一个函数而不是对象(通过 babel/babel#6494)。这将有助于我们进行缓存。

解析基于字符串的配置值

在 Babel 6 中,直接传递给 Babel 的值(而非来自配置文件)是相对于被编译的文件进行解析的,这导致了很多混淆。

在 Babel 7 中,这些值将一致地相对于加载它们的配置文件或工作目录进行解析。

对于 presetsplugins 值,这一改变意味着 CLI 在以下情况下会表现得更好:

Shell
babel --presets @babel/preset-env ../file.js

假设你的 node_modules 文件夹位于当前目录(.),在 Babel 6 中这会失败,因为找不到预设。

这一改变也影响了 onlyignore,我们将在接下来详细说明。

基于路径的 onlyignore 模式

在 Babel 6 中,onlyignore 被视为普通匹配字符串,而非文件路径通配符。这意味着例如 *.foo.js 会匹配 ./**/*.foo.js,这对大多数用户来说是令人困惑和意外的。

在 Babel 7 中,这些现在被视为基于路径的通配模式,可以是相对路径或绝对路径。这意味着如果你之前使用这些模式,现在可能需要至少添加一个 **/ 前缀,以确保模式能深度匹配目录中的文件。

onlyignore 模式仍然适用于目录,因此你也可以使用 only: './tests' 来仅编译 tests 目录中的文件,而无需使用 **/*.js 来匹配所有嵌套文件。

Babel 的 CLI 命令

babel 命令的 --copy-files 参数(用于复制 Babel 无法处理的目录文件)现在会同时复制未通过 only/ignore 检查的文件,而此前会静默跳过所有被忽略文件。

@babel/node

Babel 6 中的 babel-node 命令属于 babel-cli 包。在 Babel 7 中,该命令已拆分为独立的 @babel/node 包,因此若需使用该命令,请添加此新依赖项。

@babel/runtime, @babel/plugin-transform-runtime

我们已将 Babel 的辅助函数与运行时的 "polyfilling" 行为分离,详见 PR

@babel/runtime 现在仅包含辅助函数。如需 core-js 支持,可使用 @babel/runtime-corejs2 及其转换选项。两种方案仍需依赖 @babel/plugin-transform-runtime

仅辅助函数

Shell
# install the runtime as a dependency
npm install @babel/runtime
# install the plugin as a devDependency
npm install @babel/plugin-transform-runtime --save-dev
babel.config.json
{
"plugins": ["@babel/plugin-transform-runtime"]
}

辅助函数 + core-js 的 polyfill

若需在 transform-runtime 中使用 core-js,现在需传递 corejs 选项并使用 @babel/runtime-corejs2 依赖项替代 @babel/runtime

Shell
# install the runtime as a dependency
npm install @babel/runtime-corejs2
# install the plugin as a devDependency
npm install @babel/plugin-transform-runtime --save-dev
{
"plugins": [
- ["@babel/plugin-transform-runtime"],
+ ["@babel/plugin-transform-runtime", {
+ "corejs": 2,
+ }],
]
}

规范合规性

@babel/plugin-proposal-object-rest-spread

对象中 RestElement 后禁止尾逗号 #290 medium

var {
- ...y, // trailing comma is a SyntaxError
+ ...y
} = { a: 1 };

由于对象展开会定义新属性(而 Object.assign 仅设置属性),Babel 已更改默认行为以更符合规范。

JavaScript
// input
z = { x, ...y };
JavaScript
// v7 default behavior: ["proposal-object-rest-spread"]
function _objectSpread(target) { ... }

z = _objectSpread({
x
}, y);
JavaScript
// Old v6 behavior: ["proposal-object-rest-spread", { "loose": true }]
function _extends(target) { ... }

z = _extends({
x
}, y);
JavaScript
// Substitute for Object.assign: ["proposal-object-rest-spread", { "loose": true, "useBuiltIns": true }]
z = Object.assign(
{
x,
},
y
);

@babel/plugin-proposal-class-properties

默认行为已更改为原先的 "spec" 模式

JavaScript
// input
class Bork {
static a = "foo";
y;
}
JavaScript
// v7 default behavior: ["@babel/plugin-proposal-class-properties"]
var Bork = function Bork() {
Object.defineProperty(this, "y", {
enumerable: true,
writable: true,
value: void 0,
});
};

Object.defineProperty(Bork, "a", {
enumerable: true,
writable: true,
value: "foo",
});
JavaScript
// old v6 behavior: ["@babel/plugin-proposal-class-properties", { "loose": true }]
var Bork = function Bork() {
this.y = void 0;
};

Bork.a = "foo";

@babel/plugin-transform-export-extensions 拆分为两个重命名提案

此变更已酝酿多时,现已最终落地

@babel/plugin-proposal-export-default-from

JavaScript
export v from "mod";

@babel/plugin-proposal-export-namespace-from

JavaScript
export * as ns from "mod";

@babel/plugin-transform-template-literals

模板字面量修订更新 #5523 low

详见 模板字面量修订提案

此问题会导致 Babel 6 抛出 Bad character escape sequence (5:6) 错误

JavaScript
tag`\unicode and \u{55}`;

该问题已在 Babel 7 中修复,现在会生成如下代码:

JavaScript
// default
function _taggedTemplateLiteral(strings, raw) {
return Object.freeze(
Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })
);
}
var _templateObject = /*#__PURE__*/ _taggedTemplateLiteral(
[void 0],
["\\unicode and \\u{55}"]
);
tag(_templateObject);
JavaScript
// loose mode
function _taggedTemplateLiteralLoose(strings, raw) {
strings.raw = raw;
return strings;
}
var _templateObject = /*#__PURE__*/ _taggedTemplateLiteralLoose(
[void 0],
["\\unicode and \\u{55}"]
);
tag(_templateObject);

常规模板字面量默认采用原先的 "spec" 模式

JavaScript
// input
`foo${bar}`;
JavaScript
// default v7 behavior: ["@babel/plugin-transform-template-literals"]
"foo".concat(bar);
JavaScript
// old v6 behavior: ["@babel/plugin-transform-template-literals", { "loose": true }]
"foo" + bar;

@babel/plugin-proposal-decorators

为适配新的装饰器提案实现,我们已将其设为默认行为。这意味着要继续使用当前装饰器语法/行为,必须将 legacy 选项设为 true

 {
"plugins": [
- "@babel/plugin-proposal-decorators"
+ ["@babel/plugin-proposal-decorators", { "legacy": true }]
]
}

注意:如果正在使用包含此插件的 @babel/preset-stage-0@babel/preset-stage-1,必须为它们传递 decoratorsLegacy 选项。

@babel/plugin-proposal-pipeline-operator

处于变化中的新提案默认会报错,在提案仍处于 < Stage 2 阶段时,需要用户明确选择特定提案。详见这篇文章

{
"plugins": [
- "@babel/plugin-proposal-pipeline-operator"
+ ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }]
]
}

已移除 babel-plugin-transform-class-constructor-call

babel-plugin-transform-class-constructor-call 已被移除 #5119 low

TC39 已决定放弃此提案。可将相关逻辑移至构造函数或静态方法中实现。

更多信息请参阅/docs/plugins/transform-class-constructor-call/

  class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}

- call constructor(x, y) {
+ static secondConstructor(x, y) {
return new Point(x, y);
}
}

let p1 = new Point(1, 2);
- let p2 = Point(3, 4);
+ let p2 = Point.secondConstructor(3, 4);

@babel/plugin-async-to-generator

我们将 babel-plugin-transform-async-to-module-method 合并到常规异步插件中,仅通过选项控制其行为。

{
"plugins": [
- ["@babel/transform-async-to-module-method"]
+ ["@babel/transform-async-to-generator", {
+ "module": "bluebird",
+ "method": "coroutine"
+ }]
]
}

babel

弃用 babel#5293 low

该包在 v6 中已提示用户安装 babel-cli 替代。不过我们认为可以赋予该名称更有趣的用途。

@babel/register

babel-core/register.js 已被移除 #5132 low

Babel 7 已移除废弃的 babel-core/register 用法,请改用独立包 @babel/register

安装 @babel/register 作为新依赖项:

npm install --save-dev @babel/register

使用 Mocha 升级:

- mocha --require babel-core/register
+ mocha --require @babel/register

@babel/register 现在仅编译当前工作目录的文件(此举修复了符号链接相关问题)。

@babel/register 的选项现在采用替换而非合并机制

@babel/generator

弃用 quotes 选项 #5154] none

如需格式化编译输出,可使用 recast/prettier/escodegen 或 fork babel-generator。

该选项在 v6.18.0 之前仅能通过显式调用 babel-generator 使用,直到我们开放了 parserOptsgeneratorOpts。因该版本存在缺陷,应无人实际在 Babel 中使用此选项。

弃用 flowUsesCommas 选项 #5123 none

目前 Flow 对象类型支持两种语法(,;)。

此项变更仅使 babel-generator 输出 , 而非 ;

@babel/core

移除 babel-core/src/api/browser.js #5124 none

babel-browser 已在 6.0 版本移除。如需在浏览器或非 Node 环境中使用 Babel,请使用 @babel/standalone

Babel 现在会返回绝对路径形式的 filename #8044

@babel/preset-env

loose(松散)模式现在会自动排除 typeof-symbol 转换(许多使用松散模式的项目已采取此做法)。