跳至主内容

7.9.0 发布:更小的 preset-env 输出、支持 TypeScript 3.8 及全新的 JSX 转换

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

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

在筹备 Babel 8.0.0 版本(将于未来几个月推出)的同时,我们刚刚完成了新的次要版本发布,其中包含针对 @babel/preset-env、TypeScript 3.8、Flow 和 JSX 的更新!

数月前,Jason Miller 开始开发 @babel/preset-modules:该项目旨在通过 module/nomodule 模式显著减小包体积。我们激动地宣布其功能现已合并至 @babel/preset-env!这意味着优化将应用于所有 preset-env 的 targets 配置值,无需额外预设。

注意:这些优化将在 Babel 8 中默认启用。在 Babel 7.9 中,您可通过向 preset-env 传入 { bugfixes: true } 选项手动启用。

此版本还全面支持引入显式类型导入/导出的 TypeScript 3.8(例如 export type { foo }),以及引入类字段 declare 修饰符的 Flow 0.120(例如 class C { declare foo: string })。

我们与 React 团队合作推出了全新的 JSX 转换,通过新增 jsx 函数(替代 React.createElement)使 React 及类 React 库能进一步优化 JSX 元素创建。

最后,@babel/parser 现支持额外的 ECMAScript 提案:Record & Tuple。请注意当前仅提供解析器支持,转换功能仍在开发中。

您可在 GitHub 查阅完整更新日志。


特别感谢 React 团队(Facebook)的 Luna Ruan 贡献了新的 JSX 转换,以及 BloombergRick Button 实现了 Record & Tuple 提案的解析器支持!

若您或公司希望支持 Babel 及 JavaScript 生态发展,可通过 Open Collective 进行捐赠,更可直接参与新 ECMAScript 提案的实现!作为志愿者驱动的项目,我们依赖社区支持来服务广大 JavaScript 用户。欢迎通过 team@babeljs.io 联系我们!

@babel/preset-envbugfixes 选项 (#11083)

@babel/preset-env 中的新选项 bugfixes 将直接取代 @babel/preset-modules 的使用。

若需深入了解此变更解决的问题背景,建议收听(或阅读)Jason近期参与的播客节目:#2 现代JavaScript与preset-env的未来#3 编译您的依赖项

此前,@babel/preset-env(及Babel插件)将ECMAScript语法特性归类为若干紧密关联的小功能集合。这些分组可能包含大量边界情况,例如"函数参数"分组及其插件就涵盖了解构、默认值和剩余参数。

基于此分组机制,Babel会根据您在@babel/preset-envtargets选项中指定的浏览器支持目标,启用或禁用对应分组。

问题在于:若目标浏览器列表中存在任何由现代语法触发的缺陷,当时我们考虑的唯一解决方案就是启用能修复该缺陷的对应转换分组。

随时间推移,更多缺陷被陆续发现并反馈到我们的issue中,这导致preset-env为处理边界情况而输出更多代码。最坏情况下,其输出结果等同于将所有代码编译到ES5——而这正是preset-env诞生所要避免的。

当启用bugfixes: true选项时,@babel/preset-env采用不同策略:将有问题的语法转译为最接近的无缺陷现代语法

例如:所有函数参数相关的新语法特性都归入同一个Babel插件(@babel/plugin-transform-function-parameters)。当目标为edge 16时,该浏览器存在解析箭头函数中带默认值的简写解构参数缺陷:

JavaScript
// this breaks in Edge 16:
const foo = ({ a = 1 }) => {};

// .. but this doesn't:
function foo({ a = 1, b }, ...args) {}

// ... and neither does this:
const foo = ({ a: a = 1 }) => {};

这意味着若我们将以下输入交给@babel/preset-env且目标设为Edge 16:

JavaScript
const foo = ({ a = 1 }, b = 2, ...args) => [a, b, args];

它会将代码转译为类似ES5的参数形式:

JavaScript
const foo = function foo(_ref, b) {
let { a = 1 } = _ref;

if (b === void 0) { b = 2; }

for (
var _len = arguments.length,
args = new Array(_len > 2 ? _len - 2 : 0),
_key = 2; _key < _len; _key++
) {
args[_key - 2] = arguments[_key];
}

return [a, b, args];
};

但若启用bugfixes选项,则仅转译有问题的语法部分:

JavaScript
const foo = ({ a: a = 1 }, b = 2, ...args) => [a, b, args];

您可在我们的REPL中查看此示例的实际效果

您可通过在配置文件中为@babel/preset-env添加以下选项立即启用此功能:

{
"presets": [
["@babel/preset-env", {
"targets": { "esmodules": true }, // Use the targets that you was already using
"bugfixes": true
}]
]
}
提示

当前bugfixes选项在使用esmodules: true目标时效果最佳——该目标允许您锁定支持原生ES模块的浏览器,并采用module/nomodule差异化服务模式。我们将在后续版本持续优化此功能,并计划在Babel 8中默认启用。

展望未来,我们期望与社区(包括浏览器厂商)协作,在JavaScript持续演进过程中实现此类方案的平稳运作。理想情况下,Babel将能够在新提案提出和完善阶段就参与实现并影响其发展方向,同时为现有标准抹平边界问题,最终让开发者能根据目标环境获得最小化的编译输出。

TypeScript 3.8:仅类型导入和导出 (#11171)

现在你可以显式地将导入和导出标记为仅类型(type-only),类似于 Flow 中已有的功能:

import type { SomeThing } from "./some-module.js";

export type { SomeThing };

通过这种方式,Babel 可以安全地判断哪些导入或导出用于类型,哪些用于值。

由于 Babel 不会分析类型,并且以单个文件为基础进行处理(类似于 TypeScript 的 --isolatedModules 选项),直到现在,@babel/plugin-transform-typescript 都将未用作值的导入视为仅类型导入。

从 Babel 7.9 开始,你可以在无需任何配置更改的情况下使用新的 type 修饰符。

我们建议配置 @babel/preset-typescript@babel/plugin-transform-typescript,使其仅在存在显式的 type 关键字时才将导入视为仅类型,类似于 TypeScript 的 --importsNotUsedAsValues preserve 选项:

babel.config.json
{
"presets": [
["@babel/preset-typescript", {
"onlyRemoveTypeImports": true
}]
]
}
提示

这些功能由 Babel 团队共同贡献,以及 Siddhant N Trivedi。如果你有兴趣了解实现细节,请查看我们在 YouTube 上 的分享!

Flow declare 字段 (#11178)

类字段提案规定未初始化的类字段会被初始化为 undefined:这与 Babel 处理 Flow 的方式不同,因为 Babel 会直接忽略它们。

因此,Flow 团队为类字段添加了 declare 修饰符的支持:

JavaScript
class Foo {
x: ?string; // A "real" field
declare y: number; // A type-only field
}

在上面的例子中,只有 y 会被 Babel 完全移除。

为了避免破坏性变更,我们通过一个标志 "allowDeclareFields" 引入了对类字段中 declare 的支持,该标志由 @babel/plugin-transform-flow@babel/preset-flow 提供支持。这将成为 Babel 8 的默认行为,因此建议你迁移配置以使用它:

babel.config.json
{
"presets": [
["@babel/preset-flow", {
"allowDeclareFields": true
}]
]
}

新的 JSX 转换 (#11154)

React 团队在去年二月创建了一份 RFC,以讨论简化元素创建。

在未来的稳定版中,React 将支持一组新的函数来实例化 JSX 元素,以替代传统的通用 React.createElement 函数。这将使得未来能够更好地优化它们。

提示

虽然它尚未在稳定版中发布,但你可以在 React 的 实验性 发布频道中尝试:

Shell
npm install react@experimental react-dom@experimental

我们与团队合作完成了一个新的转换,支持将 JSX 编译为这些新函数。它还会在需要时自动导入 "react"(或其他支持新 API 的库),因此你不再需要手动引入。

例如,以下输入:

JSX
function Foo() {
return <div />;
}

会变成:

JSX
import { jsx as _jsx } from "react/jsx-runtime";
function Foo() {
return _jsx("div", ...);
}

注意: react/jsx-runtimereact/jsx-dev-runtime 中的函数并不打算在 @babel/plugin-transform-react-jsx@babel/plugin-transform-react-jsx-development 插件之外使用。

总结如下(更多细节请查阅 RFC),该转换将:

  • 始终将 children 作为 props 传递

  • key 与其他 props 分开传递

  • 在开发环境(DEV)中,

    • 传递标志位用于判断元素是否静态
    • __source__self 与其他属性分开传递

使用方法:通过向 @babel/preset-react(或 @babel/plugin-transform-react-jsx)传递 { "runtime": "automatic" }(而非 "classic")即可启用此新转换:

babel.config.json
{
"presets": [
["@babel/preset-react", {
"runtime": "automatic"
}]
]
}

从 Babel 8 开始,"automatic" 将成为默认运行时

您也可以通过新的 @babel/plugin-transform-react-jsx-development 转换器,或向 @babel/preset-react 传递 { "development": true, "runtime": "automatic" } 来为此新转换启用开发模式

您可在文档中阅读有关此新转换的更多信息