跳至主内容

7.4.0 发布:支持 core-js 3、静态私有方法与部分应用

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

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

今天我们正式发布 Babel 7.4.0!

本次更新包含对 TypeScript 3.4 的支持、函数调用中的部分应用提案,以及静态私有方法功能。

我们在 @babel/parser 中增加了对语义化括号表达式的支持,并使其规范兼容性达到_空前_高度!

最后同样重要的是:@babel/preset-env@babel/transform-runtime 现已支持 core-js@3,同时 @babel/template 新增了超赞的语法特性!

完整更新日志请查看 GitHub


特别感谢所有新加入的 Babel 贡献者 😊。自从我们改用 GitHub Actions 生成更新日志后,未能及时在每次发布中致谢,但自 Babel 7.3.3 以来涌现了大量贡献者!

本版本的诸多功能由赞助商协作开发。Bloomberg 自 7.0 起每个版本都贡献了新型私有元素支持(7.17.27.3),本次更实现了静态私有方法!目前仅剩静态私有 getter/setter 待实现。

同样地,TrivagoOpenCollective 的基础支持赞助商)承接了部分应用插件的开发工作。

过去一个月,我们尝试与企业直接合作开发社区受益功能:RunKit 赞助 Nicolò 实现了 @babel/template 的占位符支持。

在管理大型开源项目时,并非所有工作都涉及代码:我们需要管理服务器、持续集成系统、社交媒体账户以及...大量密码!我们衷心感谢 1Password 接纳我们加入其开源支持计划,并为我们提供了免费的 1Password Teams 账户!

如果您或您的公司希望支持 Babel 和 JavaScript 生态的发展但不知从何入手,可以通过 OpenCollective 进行捐赠,更棒的是直接参与新 ECMAScript 提案的实现!作为志愿者驱动的项目,我们依赖社区支持来资助广大 JavaScript 用户群体,同时维护代码所有权。欢迎通过 henry@babeljs.io 联系 Henry 进一步交流!

core-js 3 (#7646)

我们因 @babel/preset-env 收获了许多赞誉,但这些荣誉更应归于 Denis 的卓越工作。他维护的 core-js@babel/polyfill@babel/runtime@babel/preset-env 提供了所有垫片支持。

core-js@3 刚刚发布,包含大量新特性:您可以通过 "core-js@3, babel and a look into the future" 了解详情。除支持所有新提案外,现在还能通过 @babel/plugin-transform-runtime 为转换后的实例方法提供垫片,实现在旧版浏览器中无污染地使用:

JavaScript
// 'foo' could be either a string or an array, or a custom object
foo.includes("a");

// ⮕

import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";

_includesInstanceProperty(foo).call(foo, "a");

@babel/preset-env@babel/plugin-transform-runtime 现已支持提案的垫片:只需在配置中将 corejs: 3 替换为 corejs: { version: 3, proposals: true }。请注意 ECMAScript 提案具有不稳定性,未来可能在 core-js@4 中发生变化!

此前 @babel/preset-env 完全依赖 compat-table 数据确定特定环境需加载的垫片。core-js@3 引入了自带兼容性数据集及完整测试套件,将显著提升垫片精确度!

core-js@2 迁移

由于 core-js 的版本 23 互不兼容(避免破坏现有代码),新版不会默认启用。

  • If you are using @babel/preset-env, you need to enable the corejs: 3 option:

    JavaScript
    presets: [
    ["@babel/preset-env", {
    useBuiltIns: "usage", // or "entry"
    corejs: 3,
    }]
    ]

    Don't forget to update your installed version of core-js!

    npm install --save core-js@3

    When using core-js 3, the useBuiltIns: "entry" option not only transforms import "core-js" imports, but also regenerator-runtime/runtime and all the nested core-js entry points.

    For example, when targeting Chrome 72, it will apply this transformation:

    Input
    JavaScript
    import "core-js/es";
    import "core-js/proposals/set-methods";
    import "core-js/features/set/map";
    Output
    JavaScript
    import "core-js/modules/es.array.unscopables.flat";
    import "core-js/modules/es.array.unscopables.flat-map";
    import "core-js/modules/es.object.from-entries";
    import "core-js/modules/esnext.set.difference";
    import "core-js/modules/esnext.set.intersection";
    import "core-js/modules/esnext.set.is-disjoint-from";
    import "core-js/modules/esnext.set.is-subset-of";
    import "core-js/modules/esnext.set.is-superset-of";
    import "core-js/modules/esnext.set.map";
    import "core-js/modules/esnext.set.symmetric-difference";
    import "core-js/modules/esnext.set.union";

    Unlike when using core-js 2, it doesn't transform @babel/polyfill imports because when used directly that package loads version 2 of core-js.

  • 若使用 @babel/plugin-transform-runtime,需启用 corejs: 3 选项:

    JavaScript
    plugins: [
    ["@babel/transform-runtime", {
    corejs: 3,
    }]
    ]

    可移除 @babel/runtime-corejs2,但需安装 @babel/runtime-corejs3

    npm remove @babel/runtime-corejs2
    npm install --save @babel/runtime-corejs3
  • @babel/polyfill 并非插件或预设,而是运行时包:若添加选项在 core-js@2core-js@3 间切换,则需同时包含两个版本的包到最终产物。因此我们决定弃用它:现在你应当手动加载 core-js 作为 polyfill,并在转换生成器时加载 regenerator-runtime/runtime

    JavaScript
    // before
    import "@babel/polyfill";

    // after
    import "core-js/stable";
    import "regenerator-runtime/runtime";

    这种方式让你能自由选择版本,并可独立更新这两个包。

    若感兴趣,可查看 @babel/polyfillcore-js@2 的旧版源码:packages/babel-polyfill/src/index.js

部分应用 (#9343#9474)

本次发布包含对部分应用提案@babel/parser 与转换支持,该提案目前处于 Stage 1 阶段(上次更新于2018年7月)。所有实现工作由 Behrang Yarahmadi 完成,并由 Trivago 赞助。

此新特性允许你绑定函数的某些参数和 this 接收器,类似于现有的 Function#bind 方法但限制更少。

JavaScript
const half = divide(?, 2); // half = num => divide(num, 3)
half(6); // 3


element.addEventListener(
"click",
// handleEvent will be called with the correct "this".
this.handleEvent("click", ?) // e => this.handleEvent("click", e)
);

在与管道操作符提案(尤其是"minimal"或"F-sharp"变体)结合使用时特别有用,可避免大量箭头函数:

JavaScript
let newScore = player.score
|> add(?, 7)
|> clamp(0, 100, ?);

// Without this proposal:
let newScore = player.score
|> (_ => add(_, 7))
|> (_ => clamp(0, 100, _));

通过在配置中添加 @babel/plugin-proposal-partial-application 插件,或在线 REPL 中启用 stage 1 预设即可体验!

:::注意 尽管提案的README也描述了标签模板字面量的部分应用,但该功能尚未实现,因为它可能会被移除。 :::

静态私有方法 (#9446)

JavaScript
class Person {
static #is(obj) {
return obj instanceof Person;
}

constructor(name) {
if (Person.#is(name)) {
throw "It is already a person!";
}
}
}

再次感谢 Tim(Bloomberg)实现此提案!

若已使用实例私有方法,无需额外配置即可使用此新特性;否则需在插件列表中添加 @babel/plugin-proposal-private-methods。在线 REPL 中通过 stage-3 预设即可启用。

类私有特性支持距离全面完成仅差一步!😄

Class PrivateInstanceStatic
Fields
class A { #a = 1 }
7.0.07.1.0
Methods
class A { #a() {} }
7.2.07.4.0
Accessors
class A { get #a() {} }
7.3.0✖️

TypeScript 3.4 RC 支持 (#9529#9534)

TypeScript 3.4 RC 版本已于近日发布,感谢 Tan Li Hau 的贡献,Babel 已支持此版本!

类型注解新增两项特性:const 上下文(将对象标记为"深度冻结")及数组/元组的 readonly 修饰符。

JavaScript
const student = {
name: "Joe Blogs",
marks: [25, 23, 30]
} as const;

const vowels: readonly string[] = ["a", "e", "i", "o", "u"];

请注意,TypeScript 3.4 RC 并非稳定版本,建议等待 TypeScript 3.4 正式发布:您可订阅 TypeScript 博客获取发布通知。🙂

带括号表达式 (#8025)

括号通常对 JavaScript 编译器或代码生成器没有实质意义:它们仅是向解析器提示某些节点具有不同优先级的"标记符":

Code1 + 2 * 3 / 1 + (2 * 3)(1 + 2) * 3
AST structure

AST 生成后,操作优先级由树形结构而非原始括号决定:因此 Babel 此前未跟踪括号信息。

当打印 AST 时,@babel/generator 无法获知原始格式,仅会在必要时生成括号。

某些情况下这会给用户带来问题。例如使用 Google Closure Compiler 时,括号用于标记类型转换表达式

JavaScript
/** @type {!MyType} */ (valueExpression)

我们已有表示括号的 ParenthesizedExpression 节点,但此前 @babel/parser 从未生成该节点,只能通过自定义插件注入。感谢 Erik Arvidsson 的工作,您现在可通过 parser 选项 启用 createParenthesizedExpressions 自动跟踪括号!

Code1 + (2 * 3)(1 + 2) * 3
AST structure

@babel/parser 规范合规性

Daniel 持续提升 @babel/parser 对 ECMAScript 规范的遵循度:现已通过 Test262 测试套件 98.97% 的用例。😎

本版本使 @babel/parser 支持识别 JavaScript 作用域规则:可准确获知变量声明状态、冲突检测、变量提升机制及特定语法结构在上下文中的合法性。

下列所有无效示例现均会被正确报错,无需在使用 @babel/parser 的工具中手动禁用:

JavaScript
let a, a; // Duplicate declaration 💥

if (foo) {
if (bar) { var b }
let b; // Duplicate declaration, again 💥
}

export { Foo }; // Error, Foo is not declared ❓

class C {
constructor() {
super(); // Super in a non-derived class 🤔
}

method(d, d) {} // Duplicate parameter 💥
}

代码占位符 (#9364)

代码并非总需人工直接编写:若需通过预定义模板生成代码呢?

模板文件常用于生成 HTML 代码,无论是使用 PHP 类语言还是 Handlebars 类模板引擎:

<!-- PHP -->
<section>
<h1><?= $title ?></h1>
<main><?= $body ?></main>
</section>

<!-- Handlebars -->
<section>
<h1>{{ title }}</h1>
<main>{{ body }}</main>
</section>

如果您开发过 Babel 插件,可能使用过 @babel/template:该工具可实现类似功能,但生成的是 JavaScript 代码:

JavaScript
const buildAssign = template`
var NAME = VALUE;
`;

const result = buildAssign({
NAME: varName,
VALUE: varValue,
});

此前 @babel/template 使用大写标识符作为"占位符"供后续替换。此方案在多数场景有效,但存在局限:

  • 默认将所有大写标识符视为占位符,未替换时 @babel/template 会抛出错误

  • 无法在标识符非法位置(如函数体或导出声明处)插入占位符

为解决这些问题,我们引入了可替换任意节点的全新语法元素:%%placeholder_name%%

JavaScript
const buildLazyFn = template`
function %%name%%(%%params%%) {
return function () %%body%%;
}
`;

const result = buildLazyFn({
name: functionName,
params: functionParams,
body: functionBody,
});

此功能由 Stripe(通过 Runkit)赞助。我们正探索新的 Babel 赞助模式——这是首次由公司直接支付团队成员薪酬来实现特定功能。若贵公司希望赞助 ECMAScript 提案实现或 Babel 通用改进,请联系我们!


在 Twitter 上讨论