React 预览版:在 Meta 之外实现增量功能发布

2023年5月3日,作者:Dan AbramovSophie AlpertRick HanlonSebastian MarkbågeAndrew Clark

🌐 May 3, 2023 by Dan Abramov, Sophie Alpert, Rick Hanlon, Sebastian Markbåge, and Andrew Clark


我们希望为 React 社区提供一个选项,使其能够在新功能设计接近最终版本时就采用这些单独的新功能,而无需等到稳定版本发布——这类似于 Meta 长期以来在内部使用 React 的前沿版本。我们正在推出一个新的官方支持的 Canary 发行通道。它允许像框架这样的精选设置将单独的 React 功能采用与 React 发布计划解耦。

🌐 We’d like to offer the React community an option to adopt individual new features as soon as their design is close to final, before they’re released in a stable version—similar to how Meta has long used bleeding-edge versions of React internally. We are introducing a new officially supported Canary release channel. It lets curated setups like frameworks decouple adoption of individual React features from the React release schedule.


tl;dr

  • 我们正在为 React 推出官方支持的 Canary 发布渠道。由于这是官方支持的,如果有任何回归出现,我们将以类似于稳定版本中 bug 的紧急程度来处理它们。
  • Canaries 让你在新特性进入语义化版本稳定发布之前,就可以开始使用单独的 React 新特性。
  • Experimental通道不同,React Canaries只包含我们合理认为已准备好被采用的功能。我们鼓励框架考虑打包固定版本的Canary React发布。
  • 我们将在博客上宣布重大更改和新功能,当它们出现在 Canary 版本中时。
  • 一如既往,React 在每个稳定版本中都遵循语义化版本控制(semver)。

React 功能通常是如何开发的

🌐 How React features are usually developed

通常,每个 React 功能都经历了相同的阶段:

🌐 Typically, every React feature has gone through the same stages:

  1. 我们开发了一个初始版本,并在其前缀加上 experimental_unstable_。该功能仅在 experimental 发布渠道中可用。目前,预计该功能将发生显著变化。
  2. 我们找到 Meta 的一个团队愿意帮助我们测试此功能并提供反馈。这导致了一轮修改。随着该功能变得更加稳定,我们与 Meta 的更多团队合作进行试用。
  3. 最终,我们对设计感到自信。我们从 API 名称中移除前缀,并默认在大多数 Meta 产品使用的 main 分支上提供该功能。此时,Meta 的任何团队都可以使用该功能。
  4. 随着我们对方向建立信心,我们也会发布关于新功能的 RFC。在这一点上,我们知道设计适用于广泛的情况,但我们可能会做一些最后的调整。
  5. 当我们接近发布开源版本时,我们会为该功能编写文档,并最终在稳定的 React 版本中发布该功能。

这个操作手册适用于我们迄今发布的大多数功能。然而,从功能一般准备好使用(步骤3)到在开源中发布(步骤5)之间,可能会存在显著的时间差。

🌐 This playbook works well for most features we’ve released so far. However, there can be a significant gap between when the feature is generally ready to use (step 3) and when it is released in open source (step 5).

我们希望为 React 社区提供一个选择,让大家可以像 Meta 一样,尽早采用单个新功能(在其可用时),而无需等待 React 的下一个发布周期。

一如既往,所有 React 功能最终都会进入稳定版本。

🌐 As always, all React features will eventually make it into a Stable release.

我们能否只做更多的小型版本更新?

🌐 Can we just do more minor releases?

通常,我们确实使用小版本发布来引入新功能。

🌐 Generally, we do use minor releases for introducing new features.

然而,这并不总是可能的。有时候,新功能与其他尚未完全完成且我们仍在积极迭代的新功能相互关联。我们无法单独发布它们,因为它们的实现是相关的。我们无法单独为它们版本控制,因为它们影响相同的包(例如,reactreact-dom)。我们还需要保持对尚未准备好的部分进行迭代的能力,而不需要像语义化版本控制要求的那样频繁发布大版本。

🌐 However, this isn’t always possible. Sometimes, new features are interconnected with other new features which have not yet been fully completed and that we’re still actively iterating on. We can’t release them separately because their implementations are related. We can’t version them separately because they affect the same packages (for example, react and react-dom). And we need to keep the ability to iterate on the pieces that aren’t ready without a flurry of major version releases, which semver would require us to do.

在 Meta,我们通过从 main 分支构建 React,并每周手动将其更新到特定的固定提交,从而解决了这个问题。这也是过去几年 React Native 版本一直遵循的方法。每一个 稳定 版本的 React Native 都固定在 React 仓库的 main 分支的某个特定提交上。这使得 React Native 能在框架层面上引入重要的 bug 修复,并逐步采纳新的 React 特性,而不会受全球 React 发布计划的影响。

🌐 At Meta, we’ve solved this problem by building React from the main branch, and manually updating it to a specific pinned commit every week. This is also the approach that React Native releases have been following for the last several years. Every stable release of React Native is pinned to a specific commit from the main branch of the React repository. This lets React Native include important bugfixes and incrementally adopt new React features at the framework level without getting coupled to the global React release schedule.

我们希望将这个工作流程提供给其他框架和精选的设置。例如,它允许一个基于 React 的框架在此重大更改被包含到稳定的 React 版本之前,先包含与 React 相关的重大更改。这尤其有用,因为一些重大更改只影响框架集成。这允许框架在自身的小版本中发布这样的更改,而不会破坏语义化版本控制(semver)。

🌐 We would like to make this workflow available to other frameworks and curated setups. For example, it lets a framework on top of React include a React-related breaking change before this breaking change gets included into a stable React release. This is particularly useful because some breaking changes only affect framework integrations. This lets a framework release such a change in its own minor version without breaking semver.

使用 Canaries 通道的滚动发布将使我们能够拥有更紧密的反馈循环,并确保新功能在社区中得到全面测试。这个工作流程更接近 TC39(JavaScript 标准委员会)如何在编号阶段处理变更。新的 React 功能可能会在基于 React 构建的框架中先于 React 稳定版本提供,就像新的 JavaScript 功能先在浏览器中发布,然后才被正式批准为规范的一部分一样。

🌐 Rolling releases with the Canaries channel will allow us to have a tighter feedback loop and ensure that new features get comprehensive testing in the community. This workflow is closer to how TC39, the JavaScript standards committee, handles changes in numbered stages. New React features may be available in frameworks built on React before they are in a React stable release, just as new JavaScript features ship in browsers before they are officially ratified as part of the specification.

为什么不使用实验版本呢?

🌐 Why not use experimental releases instead?

尽管你可以从技术上使用实验版本,但我们建议不要在生产环境中使用它们,因为实验 API 在稳定化过程中可能会经历重大破坏性更改(甚至可能被完全删除)。虽然预览版版本也可能包含错误(与任何版本一样),但未来我们计划在博客上宣布预览版版本中的任何重大破坏性更改。预览版版本最接近 Meta 内部运行的代码,因此你通常可以期望它们相对稳定。然而,当在固定的提交版本之间更新时,你确实需要保持版本固定,并手动扫描 GitHub 提交日志。

🌐 Although you can technically use Experimental releases, we recommend against using them in production because experimental APIs can undergo significant breaking changes on their way to stabilization (or can even be removed entirely). While Canaries can also contain mistakes (as with any release), going forward we plan to announce any significant breaking changes in Canaries on our blog. Canaries are the closest to the code Meta runs internally, so you can generally expect them to be relatively stable. However, you do need to keep the version pinned and manually scan the GitHub commit log when updating between the pinned commits.

**我们预计大多数在经过策划的环境(如框架)之外使用 React 的人,会希望继续使用稳定版本。**然而,如果你正在构建一个框架,你可能想考虑打包一个固定在特定提交的 React Canary 版本,并按照你自己的节奏进行更新。这么做的好处是,它可以让你更早地向用户交付已完成的 React 功能和 bug 修复,并按照你自己的发布计划发布,这类似于 React Native 在过去几年中的做法。缺点是,你需要承担额外的责任来审核哪些 React 提交被纳入,并向你的用户传达你的发布中包含了哪些 React 变更。

如果你是框架作者并且想尝试这种方法,请联系我们。

🌐 If you’re a framework author and want to try this approach, please get in touch with us.

提前宣布重大变更和新功能

🌐 Announcing breaking changes and new features early

预览版发布代表了我们在任何给定时间对下一次稳定版 React 发布的最佳猜测。

🌐 Canary releases represent our best guess of what will go into the next stable React release at any given time.

传统上,我们只会在发布周期的末尾(在进行重大版本发布时)宣布重大变更。现在,由于 Canary 版本已成为官方支持的 React 使用方式,我们计划转向在 Canary 中发布时宣布重大变更和重要新功能。例如,如果我们合并了一个将在 Canary 中发布的重大变更,我们会在 React 博客上撰写一篇文章,包括必要的代码迁移工具和迁移说明。然后,如果你是一名框架作者,在发布一个将固定 React Canary 包含该变更的重大版本时,你可以在发行说明中链接到我们的博客文章。最后,当 React 的稳定重大版本准备就绪时,我们将链接到那些已经发布的博客文章,希望能够帮助我们的团队更快地取得进展。

🌐 Traditionally, we’ve only announced breaking changes at the end of the release cycle (when doing a major release). Now that Canary releases are an officially supported way to consume React, we plan to shift towards announcing breaking changes and significant new features as they land in Canaries. For example, if we merge a breaking change that will go out in a Canary, we will write a post about it on the React blog, including codemods and migration instructions if necessary. Then, if you’re a framework author cutting a major release that updates the pinned React canary to include that change, you can link to our blog post from your release notes. Finally, when a stable major version of React is ready, we will link to those already published blog posts, which we hope will help our team make progress faster.

我们计划在 API 推送到 Canary 版本时进行文档记录——即使这些 API 尚未在其他版本中可用。仅在 Canary 版本中可用的 API 将在相应页面上标注特别说明。这将包括像 use 这样的 API,以及其他一些 API(如 cachecreateServerContext),我们将为它们发送 RFC。

🌐 We plan to document APIs as they land in Canaries—even if these APIs are not yet available outside of them. APIs that are only available in Canaries will be marked with a special note on the corresponding pages. This will include APIs like use, and some others (like cache and createServerContext) which we’ll send RFCs for.

预览版必须被固定

🌐 Canaries must be pinned

如果你决定为你的应用或框架采用 Canary 工作流,请确保始终固定你正在使用的 Canary 的确切版本。由于 Canary 是预发布版本,它们仍可能包含破坏性更改。

🌐 If you decide to adopt the Canary workflow for your app or framework, make sure you always pin the exact version of the Canary you’re using. Since Canaries are pre-releases, they may still include breaking changes.

示例:React 服务器组件

🌐 Example: React Server Components

正如我们在三月宣布的那样,React 服务器组件的规范已经最终确定,我们预计与其面向用户的 API 合同相关不会有重大破坏性更改。然而,我们还不能在 React 的稳定版本中发布对 React 服务器组件的支持,因为我们仍在开发一些相互交织的仅框架功能(例如资源加载),并且预计在那里会有更多破坏性更改。

🌐 As we announced in March, the React Server Components conventions have been finalized, and we do not expect significant breaking changes related to their user-facing API contract. However, we can’t release support for React Server Components in a stable version of React yet because we are still working on several intertwined framework-only features (such as asset loading) and expect more breaking changes there.

这意味着 React 服务器组件已准备好被框架采用。然而,在下一个主要的 React 版本发布之前,框架采用它们的唯一方式是发布一个固定的 Canary 版本的 React。(为了避免打包两个 React 副本,希望这样做的框架需要强制将 reactreact-dom 解析到他们随框架一起发布的固定 Canary 版本,并向用户说明。举例来说,这就是 Next.js App Router 所做的事情。)

🌐 This means that React Server Components are ready to be adopted by frameworks. However, until the next major React release, the only way for a framework to adopt them is to ship a pinned Canary version of React. (To avoid bundling two copies of React, frameworks that wish to do this would need to enforce resolution of react and react-dom to the pinned Canary they ship with their framework, and explain that to their users. As an example, this is what Next.js App Router does.)

在稳定版和预览版版上测试库

🌐 Testing libraries against both Stable and Canary versions

我们并不指望库的作者测试每一个 Canary 版本,因为这将非常困难。然而,就像三年前我们首次引入不同的 React 预发布通道时一样,我们鼓励库在最新的 Stable 版本和最新的 Canary 版本上都运行测试。如果你发现行为上的变化没有被提前宣布,请在 React 仓库中提交一个 bug,以便我们帮助诊断。我们预计,随着这种做法的广泛采用,将减少将库升级到 React 新的主要版本所需的工作量,因为在新的版本发布时意外的回归问题便会被发现。

🌐 We do not expect library authors to test every single Canary release since it would be prohibitively difficult. However, just as when we originally introduced the different React pre-release channels three years ago, we encourage libraries to run tests against both the latest Stable and latest Canary versions. If you see a change in behavior that wasn’t announced, please file a bug in the React repository so that we can help diagnose it. We expect that as this practice becomes widely adopted, it will reduce the amount of effort necessary to upgrade libraries to new major versions of React, since accidental regressions would be found as they land.

注意

严格来说,Canary 并不是一个新的发布渠道——它以前被称为 Next。不过,我们决定重新命名它,以避免与 Next.js 混淆。我们将其宣布为一个新的发布渠道,以传达新的期望,例如 Canaries 是使用 React 的官方支持方式。

🌐 Strictly speaking, Canary is not a new release channel—it used to be called Next. However, we’ve decided to rename it to avoid confusion with Next.js. We’re announcing it as a new release channel to communicate the new expectations, such as Canaries being an officially supported way to use React.

稳定版本像以前一样工作

🌐 Stable releases work like before

我们不会对稳定的 React 版本进行任何更改。

🌐 We are not introducing any changes to stable React releases.