7.4.0发布:core-js 3,静态私有方法和部分应用

129 阅读9分钟

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

今天我们发布了Babel 7.4.0!

这个版本包括对TypeScript 3.4的支持,对函数调用中部分应用的建议,以及静态私有方法。

我们在@babel/parser ,增加了对有意义的括号表达式的支持,同时也使其比以往更符合规范!

最后但并非最不重要的是,@babel/preset-env@babel/transform-runtime 现在都支持core-js@3 ,而@babel/template 也有了一些可爱的新语法。

你可以在GitHub上阅读整个更新日志。


特别感谢所有新的Babel贡献者😊。自从我们开始使用GitHub动作生成版本更新日志以来,我们还没有机会在每个版本中感谢他们,但从Babel 7.3.3开始,他们已经是很多人了

本版本中的许多功能都是与我们的赞助商合作开发的。 彭博社自7.0以来的每个版本(7.17.27.3)都支持一种新的私有元素,而且他们现在已经实现了静态私有方法这样就只剩下静态私有的getters和setters了。

同样地。 Trivago(OpenCollective上的一个基地支持赞助商)接管了部分应用程序插件的实现。

在过去的一个月里,我们一直在尝试在各种功能/优化上更直接地与公司合作,这将有利于社区。 奔跑吧一直在赞助Nicolò@babel/template 中实现占位符支持。

当管理一个大型开源项目时,并不是所有的东西都是代码:我们需要管理服务器、持续集成、社交媒体账户和......大量的密码!我们真的很感激。我们非常感谢 1Password接受我们加入他们的开源支持计划,并为我们提供了一个免费的1Password团队账户。

如果你或你的公司想支持Babel和JavaScript的发展,但不确定如何支持,你可以在OpenCollective上向我们捐款,更好的是,你可以直接和我们一起实施新的ECMAScript提案!作为一个志愿者驱动的项目,我们依靠社区的支持,既为我们支持广大的JavaScript用户的努力提供资金,也为代码的所有权提供资金。如果你想进一步讨论,请联系Henry,henry@babeljs.io!

core-js 3

我们在@babel/preset-env 上的工作得到了很多赞誉,但其中大部分应该归功于Denis的出色工作。他维护的core-js ,提供了所有由@babel/polyfill@babel/runtime@babel/preset-env 所加载的polyfills。

core-js@3 在这里,你可以读到"core-js@3,babel和对未来的展望 "的内容。除了所有的新建议外,它还使使用 ,对转换@babel/plugin-transform-runtime 实例方法进行聚填成为可能,这样它们就可以在旧的浏览器中使用,而不会对全局环境造成污染。

// '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 和 ,现在支持polyfilling建议:你只需要在你的配置中使用 ,而不是 。请记住,ECMAScript提案本身是不稳定的,可能会在 !@babel/plugin-transform-runtime corejs: { version: 3, proposals: true } corejs: 3 core-js@4

以前,@babel/preset-env 完全依靠compat-table 的数据来确定在特定的环境下需要加载哪些polyfills。core-js@3 引入了自己的兼容性数据集和一个详尽的测试套件,应该会使polyfilling更加准确。

移植自core-js@2

由于23 的版本core-js 是不兼容的(我们不想破坏你的代码!),所以它没有默认启用。

  • 如果你正在使用@babel/preset-env ,你需要启用corejs: 3 选项。

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

    不要忘记更新你所安装的core-js 的版本 !

    npm install --save core-js@3
    

    当使用core-js 3,useBuiltIns: "entry" 选项不仅可以转换import "core-js" 的导入,还可以转换regenerator-runtime/runtime 和所有嵌套的core-js 的入口点。

    例如,当针对Chrome 72时,它将应用这种转换。

    输入

    import "core-js/es";
    import "core-js/proposals/set-methods";
    import "core-js/features/set/map";
    

    输出

    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";
    

    与使用core-js 2时不同,它不会转换@babel/polyfill 的导入,因为当直接使用该包时,会加载core-js 的第二版。

  • 如果你使用@babel/plugin-transform-runtime ,你需要使用corejs: 3 选项。

    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 这不是一个插件或预设,而是一个运行时包:如果我们添加一个选项在 和 之间切换,两个包的版本都需要包含在你的包里。出于这个原因,我们决定取消它:你现在应该为polyfills加载 ,如果你正在转换生成器,则加载 。core-js@2 core-js@3 core-js regenerator-runtime/runtime

    // before
    import "@babel/polyfill";
    
    // after
    import "core-js/stable";
    import "regenerator-runtime/runtime";
    

    这使你有能力加载你想要的任何版本,并独立地更新这两个包。

    如果你有兴趣,你应该查看@babel/polyfill 的旧源码,以获取core-js@2:packages/babel-polyfill/src/index.js

部分应用

这个版本包括对部分应用提案@babel/parser 和转化支持,该提案目前处于第一阶段(最后一次提出是在2018年7月)。所有的实现工作都是由Behrang Yarahmadi完成的,由Trivago赞助。

这个新功能允许你绑定一些参数和this 接收器函数,类似于现有的Function#bind 方法,但限制较少。

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)
);

它与管道运算符的提议(尤其是在使用 "最小 "或 "F-sharp "变体时)也确实有用,因为它使得避免大量的箭头函数成为可能。

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 ,或在在线复制中启用stage 1 预设来测试它。

ℹ️注意:尽管提案的readme也描述了部分应用于标记的模板字面,但它没有被实现,因为它可能会被删除

静态私有方法

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 。当使用在线复制时,它是由stage-3 预设启用的。

类私有特性的支持离完成只有一步之遥了!😄

班级私有化实例静态
字段
class A { #a = 1 }7.0.07.1.0
方法
class A { #a() {} }7.2.07.4.0
访问者
class A { get #a() {} }7.3.0:heavy_multiplication_x:

支持TypeScript 3.4 RC

TypeScript 3.4 RC已于几天前发布,感谢Tan Li Hau,它已经被Babel支持了

类型注释有两个新特性:const context,它将一个对象标记为 "深度冻结",以及数组和图元的readonly 修改器。

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博客,以便在发布的时候得到通知。 🙂

圆括号表达式

对于JavaScript编译器或代码生成器来说,括号通常是没有意义的:它们只是 "提示",用来告诉解析器某些节点的优先级与默认的不同。

代码1 + 2 * 3 /1 + (2 * 3)(1 + 2) * 3
AST结构

当AST生成后,操作的优先级由树状结构决定,而不是由原来的小括号决定:由于这个原因,Babel没有跟踪它们。

在打印AST时,@babel/generator ,不知道原始格式,只在需要的地方生成小括号。

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

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

我们已经有一个ParenthesizedExpression 节点来表示小括号,但它从来没有被@babel/parser 生成,它只能被自定义插件注入。感谢Erik Arvidsson的工作,现在你可以使用createParenthesizedExpressions 解析器选项来自动跟踪它们了

代码1 + (2 * 3)(1 + 2) * 3
AST结构

@babel/parser 规范遵从性

Daniel正在使@babel/parser 越来越符合ECMAScript规范:它现在已经通过了Test262套件中98.97%的测试。😎

这个版本使@babel/parser 知道JavaScript的范围规则:它现在知道哪些变量被声明,是否有冲突,它们是否被吊起,以及在发现的上下文中是否允许特定的语法结构。

所有这些无效的例子现在都被正确地报告为错误,避免了在每个使用@babel/parser 的工具中手动禁止它们。

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

代码占位符

代码并不总是要由人类直接编写的:如果一些代码需要被生成,也许是使用预定义的模板呢?

模板文件经常被用来生成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代码。

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

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

到目前为止,@babel/template 使用大写字母标识符作为 "占位符",然后需要替换。虽然这种方法在大多数情况下运行良好,但它也有一些注意事项。

  • 默认情况下,每个大写标识符都被标记为占位符,如果不替换,@babel/template ,就会产生错误。
  • 不可能在不允许使用标识符的地方放置占位符,例如在函数体或导出的声明的位置。

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

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

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

这个功能得到了Stripe(通过Runkit)的赞助。我们一直在尝试新的赞助Babel的方式,第一次有公司直接赞助了一个功能的实施,向团队中的一个成员支付了费用。如果您的公司想赞助ECMAScript的提案,或者是Babel的一般性改进,请联系我们