移除 Babel 的阶段预设
在 v7 之后,我们决定最好停止在 Babel 中发布 Stage 预设(例如 @babel/preset-stage-0)。
🌐 Moving forward with v7, we've decided it's best to stop publishing the Stage presets in Babel (e.g. @babel/preset-stage-0).
我们不是轻率做出这个决定的,我们想展示 TC39、Babel 和社区之间互动的背景。
🌐 We didn't make this decision lightly and wanted to show the context behind the interplay between TC39, Babel, and the community.
一些历史
🌐 Some History
Babel 预设是一份可共享的插件列表。
🌐 A Babel preset is a sharable list of plugins.
官方 Babel Stage 预设 跟踪了 JavaScript 中新的 语法提案 的 TC39 阶段流程。
🌐 The official Babel Stage presets tracked the TC39 Staging process for new syntax proposals in JavaScript.
每个预设(例如 stage-3、stage-2 等)都包含了该阶段以及其上阶段的所有插件。例如,stage-2 包含了 stage-3,依此类推。
🌐 Each preset (ex. stage-3, stage-2, etc.) included all the plugins for that particular stage and the ones above it. For example, stage-2 included stage-3, and so on.
这让想要使用实验性语法的用户只需添加这个预设,而不需要配置或安装每个单独的插件。
🌐 This allowed users who wanted to use experimental syntax to simply add the preset, instead of needing to configure/install each individual plugin.
实际上,我们在 Babel v6 发布后不久就添加了 Stage 预设(在 v5 中它之前是一个配置标志)。下面显示的是来自 Babel v6 的一个较旧示例。
🌐 We actually added the Stage presets shortly after Babel's v6 release (it was previously a config flag in v5). Shown below is an older example from Babel v6.
在配置中看到这个很常见:
🌐 It was common to see this in a config:
{
"presets": ["es2015", "react", "stage-0"]
}
[babel-preset-stage-0] 的原始来源 (https://unpkg.com/babel-preset-stage-0@6.0.14/index.js) :
🌐 The original source of babel-preset-stage-0:
module.exports = {
presets: [
require("babel-preset-stage-1")
],
plugins: [
require("babel-plugin-transform-do-expressions"),
require("babel-plugin-transform-function-bind")
]
};
问题
🌐 Problems
这些预设是一个方便的方式,让我们都能使用我们想要的东西:JavaScript 那新的、闪亮的、‘尚待确定’的未来。
🌐 These presets were a convenient way to use what we all wanted: the new, shiny, "yet-to-be-determined" future of JavaScript.
回头看,效果真的很好!(是不是太好了点?)
🌐 Looking back, it worked really well! (Maybe too well?)
工作太好了?
🌐 Too Good a Job?
像 CoffeeScript 这样的语言和像 Traceur 这样的工具帮助确立了编译 JavaScript 的概念。Babel 让使用新的/未来的语法并与现有工具整合变得更容易。大家的预期也从怀疑和担忧,变成了完全接受这些实验性的东西。
🌐 Languages like CoffeeScript and tooling like Traceur helped establish the idea of compiling JavaScript. Babel made it even easier to both use new/future syntax and integrate with existing tooling. The expectations changed from skepticism and worry to completely embracing the experimental.
如果没有像 Babel 这样的编译器被广泛采用,我们可能不会有现在的局面:它加速了 ES2015 的使用(以及教学),让更多人接触到。React 的发展进一步推动了使用,因为它的 JSX 语法、类属性以及对象剩余/扩展语法让大家对这些语法提案了解得更多。
🌐 We probably wouldn't be where we are if not for the wide adoption of compilers such as Babel: it accelerated the usage (and teaching) of ES2015 to a much larger audience. The growth of React further fueled usage as its JSX syntax, class properties, and object rest/spread led to people knowing a bit more about these syntax proposals.
Babel曾经是一次性的设置,人们使用一次后就不再考虑它。它成为了底层基础设施,隐藏在其他工具背后,直到出现 SyntaxError、依赖问题或集成问题。简单地使用 stage-0 就行了。
🌐 Babel became a one-time setup for people, never to be thought of again. It became the underlying infrastructure, hidden behind other tools until there was a SyntaxError, dependency issues, or integration issues. Simply use stage-0.
在某种程度上,这真的很棒,因为这意味着这些想法正在实际环境中被测试,甚至是在生产环境中。不过,这也意味着如果一个提案发生了重大变化(甚至被完全放弃),许多公司、工具和人都会遇到一些麻烦。
🌐 This was awesome to see in some ways, as it meant that these ideas were being tested in the wild, even in production environments. However, it also meant that many companies, tools, and people would encounter some trouble if a proposal changed in a significant way (or even got dropped altogether).
来回
🌐 Back and Forth
多年来,我们提出了许多问题,讨论在 #4914、 #4955、 #7770 中该如何处理 Stage 预设。我甚至在之前关于 Babel 7.0 的文章里写过,我们不会 移除它们 😅。
🌐 Over the years, we've raised many issues to discuss what to do with the Stage presets in #4914, #4955, #7770. I even wrote in an older post about Babel 7.0 that said we weren't removing them 😅.
不过,我们发现即使是对 Babel 本身,保留 Stage 预设也会导致问题:
🌐 However, we found that keeping the Stage presets would lead to issues even for Babel itself:
- 像这样提问是很常见的问题:"使用异步函数需要哪些预设?"。人们很难确切知道
stage-0指的是什么,而且很少有人会去看它的package.json或源代码。 - 在阶段3移除一个提案插件(等它进入阶段4后)实际上是一个破坏性更改。当你试图使用
@babel/preset-env不去编译一个原生支持的提案时,这个问题会更加严重。
ES7 装饰器
🌐 "ES7 Decorators"
问题的一部分正是围绕给事物命名,正如我们常听到的,给事物命名很难。
🌐 Part of the issue is precisely around naming things, and as we often hear, naming things is hard.
ES6 本身有很多名字:Harmony、ES Next、ES6、ES2015。人们一听到新想法,通常就会选择最新的编号并给它起个名字,这很自然。
🌐 There were a lot of names for ES6 itself: Harmony, ES Next, ES6, ES2015. When people hear about new ideas it makes sense to just pick the latest number and attach the name to it.
因此,很容易搜索 关于说“ES7 装饰器”的推文/博客文章/演讲,并发现它已经成为习惯的称呼。
🌐 Therefore, it's easy to search around for tweets/blog posts/talks that say "ES7 Decorators" and find that it's become the accustomed name for it.
提醒一下,使用 :: 进行绑定只是阶段 0 的实验性提议,可能永远不会成为 JS 的一部分。别叫它“ES7”。
— Dan Abramov (@dan_abramov) 2016 年 10 月 9 日
完全可以理解这种情况在无意识中发生,但如果一直这样做,就会对语言的发展设定不同的期望。这没什么好内疚的——我们是作为一个社区一起学习,并互相提醒 JavaScript 是如何运作的。
🌐 It's completely understandable that this happens without realizing it, but continuing to do so sets different expectations for how the language progresses. It's nothing to feel guilty about — we learn as a community and remind one another of how JavaScript works.
Jay Phelps 写了一篇关于这个主题的不错的 文章。他解释说,最好按照他们当前所处的“阶段”来称呼他们:比如“阶段2装饰器”,或者干脆叫“装饰器提案”。
原因是说“ES7 装饰器”意味着装饰器被认为是 ES7 的一部分。我在我上一篇关于编译 node_modules 的帖子里提到过这一点,但处于某个阶段并不保证什么:一个提案可能会停滞、倒退,或者被完全移除。
🌐 The reasoning is that saying "ES7 Decorators" assumes that Decorators is expected to be in ES7. I mentioned this in my last post regarding compiling node_modules, but being in a particular Stage doesn't guarantee much: a proposal can stall, move backward, or get removed entirely.
当我们决定将提案插件的名字从 @babel/plugin-transform- 改为 @babel/plugin-proposal- 时,我们想强调这一点。
🌐 We wanted to highlight this fact when we decided to change the names of the proposal plugins from @babel/plugin-transform- to @babel/plugin-proposal-.
BabelScript
在流程这么早就有提案的预设可能意味着这些提案一定会推进或有一个稳定的实现方案。
🌐 Having presets for proposals so early in the process may imply that these proposals are guaranteed to move forward or have a stable implementation.
TC39 提醒在使用第二阶段或更早阶段的提案时要小心,因为这可能会导致社区无意中施加压力,让实现保持原样,而不是改进它,以免破坏现有代码或生态系统碎片化(例如,在装饰器中使用像 # 而不是 @ 的不同符号)。
人们开玩笑说,使用 Babel 的开发者实际上在使用“BabelScript”而不是 JavaScript,这暗示着一旦某个功能有了 Babel 插件,就意味着它已经“固定”或者已经是语言的官方部分了(其实并不是)。对于一些人来说,当他们看到一个新的语法/想法(阶段“-1”)时,第一反应是看有没有对应的 Babel 插件。
🌐 People joke that developers who use Babel are using "BabelScript" instead of JavaScript, implying that somehow once a Babel plugin is made for a certain feature, that must mean it’s "fixed" or officially part of the language already (which is not true). For some, the first thought for people when they see a new syntax/idea (Stage "-1") is whether there a Babel plugin for it.
设定期望
🌐 Setting Expectations
在像 Babel 这样的编译器让人们普遍编写 ES2015 之后,开发者自然会想尝试更新、更实验性的“特性”。在 Babel 中,这个过程是通过在之前的版本中使用 stage 标志或使用 stage-x 预设来实现的。
🌐 After compilers like Babel made it common practice for people to write ES2015, it was natural for developers to want to try out even newer and more experimental "features". The way this worked in Babel was to use the stage flag in previous versions or the stage-x presets.
作为加入任何新功能最方便的方式,它很快就成了人们在配置 Babel 时的默认选择(尽管在 Babel v6 中它默认变成了什么也不做,这引发了很多抱怨)。
🌐 Being the most convenient way of opting into any new feature, it quickly became the default for people when configuring Babel (even though in Babel v6 it moved to not doing anything by default, which caused lots of complaints).
在库、样板代码、演讲、推文和幻灯片中看到 "stage-0" 被使用变得很常见。
🌐 It became common to see "stage-0" being used in libraries, boilerplates, talks, tweets, and slides.
在生产环境中对
— Ryan Florence (@ryanflorence) 2015年7月31日babel?stage=0说“拒绝”。
多年前就有很多很好的讨论,但那时候浏览起来并不容易:我们不想因为用户理解各种权衡而在使用时给他们打上 console.warn,而且当时根本没有这个选项似乎也不合理。
🌐 There was a lot of good discussions even years ago, but it wasn't the easiest thing to navigate: we wouldn't want to penalize users who understood the tradeoffs by putting console.warns when using it, and not having the option at all seemed unreasonable at the time.
盲目地选择进入 Stage 0(无论我们默认是否有)或者人们主动选择这样做看起来都很危险,但完全不使用任何提案又显得过于谨慎。理想情况下,每个人都应该能够根据自己觉得合理的特性做出知情的决定,并明智地使用它们,不管它们处于哪个阶段。Mike Pennisi 写了一篇关于这些问题的很棒的文章。
🌐 Blindly opting into Stage 0 (whether we had it by default) or people choosing to do so seems dangerous, but also never using any proposals is overly cautious. Ideally, everyone should able to make an informed decision about the kinds of features that seem reasonable to them and use them wisely, regardless of what stage they are in. Mike Pennisi wrote a great post about these concerns.
我们的意图不是威胁、催促或者强行把特定的东西加入生态系统或 JavaScript,而是忠实地维护围绕新想法的实现和讨论。
🌐 It isn't our intention to threaten, rush, or force specific things into the ecosystem or JavaScript but to faithfully maintain the implementation/discussions around new ideas.
犹豫
🌐 Hesitations
其他考虑
🌐 Other Considerations
我们本来也可以尝试去:
🌐 We also could have tried to:
- 重命名预设以更好地表示稳定性等级(并不能解决版本管理问题)
- 更好的版本管理策略:独立给预设版本号,并在需要时立即更新,也许可以用
0.x - 旧版本预设的警告/错误
最终,如果我们保留这些阶段,人们仍然需要查一下哪些提案处于哪个阶段,才能知道该用哪些。
🌐 In the end, people would still have to look up what proposals are at what Stage to know which ones to use if we kept the Stages in.
为什么是现在?
🌐 Why Now?
为什么不早点移除它呢?Stage 预设已经在 Babel 中存在好多年了,而且人们担心增加更多使用 Babel 的“复杂性”。有很多工具、文档、文章和知识都是围绕 Stage 预设的。之前我们觉得最好官方维护这些预设,因为其他人迟早会(并且已经在)创建它们。
🌐 Why not remove it earlier? The Stage presets have been part of Babel for years, and there were concerns with adding more "complexity" to using Babel. A lot of tooling, documentation, articles, and knowledge were around the Stage presets. Earlier, we thought it was better to officially maintain the presets since someone else would (and will) inevitably create them.
我们正在尝试确定反馈的适当程度:如果只有委员会决定语言中包含什么内容,可能会导致规范很明确但其实不需要的特性,但如果社区期望那些进行中的、实验性的提案被认为是稳定的或者可以在生产环境中使用而不会有后果,那么我们就会遇到其他问题。我们都希望前进并有目的地推进:不是赶时间,但也不要太小心。Babel 是进行这种实验的合适地方,但了解边界在哪里是必要的。
🌐 We're trying to determine the right level of feedback: if it's only the committee that decides what goes into the language, it may lead to well-specified features that are not needed, but if the community expects that in-progress, experimental proposals are considered stable or ok to use in production without consequence, then we'll have other issues. We all want to move forward and proceed with intention: not rushing, but not being too cautious. Babel is the right place to do that experimentation but knowing where the boundaries are is necessary.
移除预设会被认为是一种“功能”,因为这意味着有人必须明确决定使用每一个提案,这对任何提案来说都是合理的,因为它们都有不同程度的不稳定性、实用性和复杂性。
🌐 Removing the presets would be considered a "feature" since it means someone would have to make an explicit decision to use each proposal, which is reasonable for any proposal since they all have varying levels of instability, usefulness, and complexity.
我们完全预料到这可能会引起一些初步的反弹,但最终我们认为长期来看,移除舞台预设对我们大家都是一个更好的决定。不过,移除之前的默认设置或舞台预设并不意味着我们不关心易用性、新用户或文档。我们会尽我们所能来保持项目稳定,提供工具让事情变得更好,并记录我们所知道的内容。
🌐 We fully expect some initial backlash from this, but ultimately feel that removing the Stage presets is a better decision for us all in the long run. However, removing previous defaults or removing the Stage presets doesn't mean we don't care about ease of use, new users, or documentation. We work with what we can to keep the project stable, provide tooling to make things better, and document what we know.
迁移中
🌐 Migrating
为了实现更自动化的迁移,我们已经更新了 babel-upgrade 来为你完成这个操作(你可以运行
npx babel-upgrade)。
TL;DR 是我们要移除 Stage 预设。在某种程度上,人们需要主动选择,并且了解正在使用哪种提案,而不是假设他们应该使用哪种,尤其是考虑到其中一些提案本身的不稳定性。如果你使用其他预设或工具链(比如 create-react-app),那么这个变化可能不会直接影响你。
🌐 The TL;DR is that we're removing the Stage presets. At some level, people will have to opt-in and know what kinds of proposals are being used instead of assuming what people should use, especially given the unstable nature of some of these proposals. If you use another preset or a toolchain, (e.g. create-react-app) it's possible this change doesn't affect you directly.
从 7.0.0-beta.52 开始,我们已经不再推荐使用 Stage 预设。如果你现在不想更改配置,我们建议你把版本 固定 到 beta.54,直到你能够升级;在 beta.54 之后,我们会抛出一个错误,并提示如何迁移。同时,在预发布阶段,确保你所有的版本都是一致的。
🌐 We have deprecated the Stage presets as of 7.0.0-beta.52. If you don't want to change your config now, we would suggest you pin the versions to beta.54 until you can upgrade; after beta.54 we will throw an error with a message saying how to migrate. And check that all your versions are the same while in prerelease.
作为一种替代方案,你可以自由创建自己的预设,包含相同的插件,并根据需要升级它们。将来,我们可能想开发一个 babel-init,可以帮助你交互式地设置插件,或者更新 babel-upgrade 本身以列出并添加当前的 Stage 插件。也许 Babel 应该保持低级工具的定位,依赖其他更高层次的框架工具,比如 create-react-app 来为用户处理这些选择。
🌐 As an alternative, you are free to make your own preset that contains the same plugins and upgrade them as you please. In the future, we may want to work on a babel-init that can help you set up plugins interactively or update babel-upgrade itself to list and add the current Stage plugins. Maybe Babel should stay as a low-level tool and rely on other higher-level/framework tools like create-react-app to handle these choices for people.
防止提案被锁定
🌐 Preventing Proposal Lock-In
James DiGioia 最近写了一篇关于使用管道操作符 (|>) 变更的 帖子。
帖子里的主要观点是,这个提案本身还在不断变化,还有几个可以探索的选项。因为我们希望将当前的三种可能性都作为 Babel 插件来实现,以便收集规范反馈和用户反馈,所以我们认为插件的使用方式也应该相应改变。这对 TC39 和 Babel 来说都是一个相对新的方法!
🌐 The main point in the post is that the proposal itself is in flux and has a few options to explore. Because we'd like to implement all three of the current possibilities as Babel plugins for both spec feedback and user feedback, we believed the way the plugin is used should change as well. This a relatively new approach for TC39 and Babel!
以前,我们只需要把 proposal 插件加到配置里,就是这样。现在,我们移除了默认行为,并要求用户选择一个标志来显示选择了哪个 proposal,同时明确说明目前没有固定的(甚至是优选的)选项。
🌐 Previously, we would add the proposal plugin to the config and that was it. Now, we remove the default behavior and ask users to opt into a flag that shows which proposal is chosen, and make it clear that there isn't a fixed (or even favored) option at the moment.
{
"plugins": [
- "@babel/plugin-proposal-pipeline-operator"
+ ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }]
]
}
这是我们希望在未来继续做的事情,作为这些提案可以接受我们所有人反馈和修改的另一个标志。移除阶段预设让这件事变得更容易,因为以前即使你不使用这种语法,我们也必须传递这些选项。
🌐 This is something that we'd like to continue to do moving forward as another indication that these proposals are open to change and feedback from all of us. The removal of the Stage presets makes this even easier as before we had to pass down these options even if you didn't use the syntax.
生态系统维护负担
🌐 Ecosystem Maintenance Burden
一种语言的“语法预算”不仅仅适用于语言本身的复杂性,还可以延伸到工具层面。每增加一种新的语法,都会给其他 JavaScript 项目的维护者带来新的负担。
🌐 A language's "syntax budget" doesn't just apply to the complexity of the language itself but can extend down to the tooling. Each new syntax addition brings new burden to the maintainers of other JavaScript projects.
一旦提出新语法,很多东西都需要更新:解析器(babylon)、语法高亮(language-babel)、代码检查工具(babel-eslint)、测试框架(jest/ava)、代码格式化工具(prettier)、代码覆盖率工具(istanbul)、压缩工具(babel-minify)等等。
🌐 Once new syntax is proposed, many things need updating: parsers (babylon), syntax highlighting (language-babel), linters (babel-eslint), test frameworks (jest/ava), formatters (prettier), code coverage (istanbul), minifiers (babel-minify), and more.
在像 acorn、eslint、jshint、typescript 等项目上,已经提出了很多问题来支持 Stage 0 提案,因为它们都在 Babel 中。没有多少项目会遵循要求它们支持任何提案的政策,因为那样维护起来会非常吃力。从很多方面来看,考虑到不断的更新和变化,我们竟然还尝试在 Babel 本身处理这些问题,真的挺让人意外的。
🌐 There have been many issues brought up on projects like acorn, eslint, jshint, typescript, and others to support Stage 0 proposals because they were in Babel. There aren't many projects that would adhere to a policy that required them to support any proposal since that would be extremely demanding to maintain. In many ways, it's surprising we even attempt to handle it in Babel itself given the constant updates and churn.
谁在做那些工作,我们有责任确保一切正常运作吗?那些项目(大多是志愿者)几乎在每个方面都缺乏帮助,但我们却不断收到各方面的抱怨。作为一个社区,我们怎么承担起管理我们基础设施的责任呢(和开源总体上没什么不同)?
🌐 Who is doing that work, and is it our responsibility to make sure everything works? Every one of those projects (mostly volunteers) is lacking in help in almost every aspect, and yet we continually get complaints about this across the board. How are we, as a community, to take responsibility for dealing with our infrastructure (not dissimilar to open source as a whole)?
Babel 承担了支持这些实验性功能的不寻常负担;与此同时,其他项目采取更保守的策略也是合理的。如果你希望在整个生态系统中看到新语言特性的支持,为 TC39 和这个项目做出贡献,把这些提案推进到第 4 阶段。
🌐 Babel has taken on the unusual burden of supporting these experimental features; at the same time, it's reasonable that other projects take a more conservative policy. If you'd like to see new language features supported across the ecosystem, contribute to TC39 and this project to bring these proposals to Stage 4.
未来
🌐 The Future
这个项目的目的是作为 TC39 流程的一部分:既为实现较新的(Stage 0-2)提案提供资源,也接收用户的反馈,同时支持 JavaScript 的旧版本。希望这篇文章能让大家更清楚地了解我们如何尽力将这个项目更好地融入 JavaScript 生态系统。我们很快就会发布 v7 的候选版本!
🌐 The purpose of this project is to act as a part of the TC39 process: being a resource for both implementing newer (Stage 0-2) proposals and receiving feedback from users while also supporting older versions of JavaScript. We hope this post sheds more light on how we are trying, as best we can, to better align this project in the JavaScript ecosystem. We will be releasing a RC for v7 soon!
如果你喜欢这篇文章以及我们在 Babel 上的工作,你可以在 Patreon 上支持我,或者让你的公司在 Open Collective 上资助我们,或者更好的是让你的公司作为工作的一部分参与到 Babel 中来。我们会非常感激这种集体的参与。
🌐 If you appreciate this post and the work we're doing on Babel, you can support me on Patreon, ask your company to sponsor us on Open Collective, or better yet get your company involved with Babel as part of your work. We'd appreciate the collective ownership.
🌐 With thanks to all the reviewers! Feel free to give feedback on Twitter.