React v16.9.0 发布及 Roadmap 最新进展

August 08, 2019 by Dan Abramov and Brian Vaughn

今天我们发布了 React 16.9。它包含几个新功能,bug 修复以及新的弃用警告,以助于筹备接下来的主要版本。

新的弃用

重命名 Unsafe 的生命周期方法

一年前,我们宣布新的 unsafe 的生命周期方法正在进行重命名:

  • componentWillMountUNSAFE_componentWillMount
  • componentWillReceivePropsUNSAFE_componentWillReceiveProps
  • componentWillUpdateUNSAFE_componentWillUpdate

React 16.9 未包含破坏性更改,并且旧的生命周期方法名在此版本继续沿用。 但当你使用旧的生命周期方法名时,你将看到如下警告:

Warning: componentWillMount has been renamed, and is not recommended for use.

正如警告所示,对于每种 unsafe 的方法来说,通常都有更好的解决方案。但你可能没有时间迁移或测试这些组件。在这种情况下,我们建议你运行一个自动重命名的 “codemod” 脚本:

cd your_project
npx react-codemod rename-unsafe-lifecycles

(请注意,这里使用了 npx,而非 npmnpx 是 Node 6+ 默认提供的实用工具)

运行 codemod 会将旧的生命周期方法名替换,例如 componentWillMount 会被替换为 UNSAFE_componentWillMount

Codemod in action

新的方法名(如 UNSAFE_componentWillMount在 React 16.9 和 React 17.x 中,仍可以继续使用。但是,新的 UNSAFE_ 前缀将有助于在代码 review 和 debug 期间,使这些有问题的字样更突出。(你也可以按照自己的意愿,在你的应用中选择性的引入 严格模式(Strict Mode) 来进一步阻止大家使用它们)。

注意

了解更多关于 版本策略及对稳定性的承诺

废弃 javascript: 形式的 URL

javascript: 开头的 URL 非常容易遭受攻击,因为它很容易在诸如 <a href> 之类的标签中引入未经过处理的输出并会造成安全漏洞:

const userProfile = {
  website: "javascript: alert('you got hacked')",
};
// 此处现在会有警告
<a href={userProfile.website}>Profile</a>

在 React 16.9 中, 这种模式将继续有效,但它将输出一个警告。如果你需使用 javascript: 形式的 URL 作为逻辑,请尝试使用 React 的事件处理程序替代。(万不得已时,你可以使用 dangerouslySetInnerHTML 来规避保护,但是这样并不被推荐且经常导致安全漏洞。)

未来的主要版本中, 如果遇到 javascript: 形式的 URL,React 将抛出错误。

废弃 “Factory” 组件

在使用 Babel 编译 JavaScript Class 成为主流前,React 支持 “factory” 组件,该组件使用 render 方法返回一个对象:

function FactoryComponent() {
  return { render() { return <div />; } }
}

这种方式令人困惑,因为它看起来像函数组件 —— 但它并不是。(函数组件只返回示例中的 <div />。)

这种方式几乎从未被广泛使用过,并且支持它会导致 React 变大且变慢。因此,我们在 16.9 中逐步弃用此模式,并在遇到时输出警告。如果项目中依赖了此组件,可以通过添加 FactoryComponent.prototype = React.Component.prototype 作为解决方案。或者你可以直接将其转换为 class 组件或函数组件。

我们不希望大多数代码库受到此影响。

新特性

用于测试的异步函数 act()

React 16.8 引入了名为 act() 的新测试实用工具来帮助你编写更符合浏览器行为的测试代码。例如,单个 act() 中的多个状态更新会进行批处理。这与 React 在处理真实浏览器事件时的工作方式相匹配,并有助于为将来 React 更频繁地批量更新组件做准备。

然而,在 16.8 中的 act() 仅支持同步函数。有时,你可能会在测试时看到类似的警告,但无法轻易修复

An update to SomeComponent inside a test was not wrapped in act(...).

在 React 16.9 中,act() 也支持异步函数, 并且你可以在调用它时使用 await

await act(async () => {
  // ...
});

这就解决了之前无法使用 act() 的情况,如当 state 更新发生在异步函数内时。因此 现在你应该能在测试中修复所有有关 act() 的警告了。

我们了解到没有足够的信息来说明如何使用 act() 来编写测试。新的测试技巧一文描述了场景的场景,以及 act() 如何帮助你编写优秀的测试。这些示例采用了原生 DOM API,但你可以使用 React Testing Library 来减少样板代码(boilerplate code)。它的许多方法已经在实现上运用了 act()

如果你遇到 act() 的相关问题,请提出 issue 告知我们,我们会尽力提供帮助。

使用 <React.Profiler> 进行性能评估

在 React 16.5 中,我们推出了新的 React Profiler for DevTools,它可以帮你找出应用程序中的性能瓶颈。在 React 16.9 中,我们还提供了一种通过编程的方式来收集测量你的代码,这种方式被称为 <React.Profiler>。我们预计大多数较小的应用都不会使用它,但在较大的应用中追踪性能回归可能会很方便。

<Profiler> 能测量 React 应用程序渲染的频率以及渲染的 “成本”。其目的是帮助标识应用程序中渲染缓慢的部分,并可能会更易于进行 memoization 等优化。

<Profiler> 可以在 React 树中的任意位置添加,以评估渲染树中对应位置的成本。 它依赖两个 props:id (string) 和 onRender 回调 (function),当树中的组件 “提交(commit)” 更新时, React 就会调用它。

render(
  <Profiler id="application" onRender={onRenderCallback}>
    <App>
      <Navigation {...props} />
      <Main {...props} />
    </App>
  </Profiler>
);

欲了解有关 Profiler 和传递给 onRender 回调参数的更多信息,请参阅 Profiler 文档

注意:

性能分析会增加额外的开销,因此它生产构建中会被禁用

如果想要在生产环境进行性能分析,React 提供了一个特殊的生成构建,并启用了分析模式。 在 fb.me/react-profiling 中阅读有关如何使用此构建的更多信息。

值得注意的 bug 修复

此版本包含一些其他显著的改进:

  • <Suspense> 树中调用 findDOMNode() 时出现崩溃的情况。已修复

  • 保留已删除的子树导致的内存泄露。已修复

  • useEffectsetState 引起的无限循环,现在会输出错误。(这与在 class 组件中的 componentDidUpdate 调用 setState 时看到的错误一致。)

感谢所有帮助解决这些问题的贡献者。你可以在此处找到完整的更新日志。

Roadmap 的进展

2018 年 11 月我们发布了 16.x 版本的 roadmap:

  • 实现 React Hooks 的 16.x 小版本(预估:Q1 2019)
  • 实现 Concurrent Mode 的 16.x 小版本 (预估:Q2 2019)
  • 实现 Suspense for Data Fetching (预估:2019 年中)

这些预估过于理想化,我们需要进行调整。

tldr: 我们按时发布了 Hook,但我们正在将 Concurrent Mode 和 Suspense for Data Fetching 重组为今年晚些时候的单个版本。

2 月份时,我们在发布的稳定 16.8 版本中引入了 React Hook,React Native 在一个月后也进行了支持。但是,我们低估了此版本的后续工作,其中包括 lint 规则,developer tools,示例以及更多文档。这使得时间线发生了改变。

现在 React Hook 已经推出,Concurrent Mode 和 Suspense for Data Fetching 的工作正在全面展开。目前正在进行 Facebook 新网站的研发工作 是建立在这些特性基础之上的。使用真实代码环境对它们进行测试有助于在影响开源用户之前发现并解决许多未知问题。其中一些修复涉及到这些特性的内部重新设计,这也导致时间的推迟。

望大家理解,这就是我们接下来的计划。

一个版本而不是两个版本

Concurrent Mode 和 Suspense 驱动了 Facebook 新网站的研发,这个项目目前在积极的开发中,因此我们可以很自信的说,从技术上讲,Concurrent Mode 和 Suspense 已近乎稳定了。同时,我们也更加清楚,在它们被开源使用之前我们应该具体做哪些步骤。

最开始,我们觉得为了实现 Data Fetching(数据请求),我们需要把 Concurrent Mode 和 Suspense 拆分为两个版本。后来我们发现,这个顺序很难说的通,因为这些特性之间的关系比我们当初想象的更为密切。因此,我们计划为 Data Fetching 发布一个同时支持 Concurrent Mode 和 Suspense 的单个版本。

我们不想再次过度承诺发布日期。考虑到我们在生产环境的代码中同时依赖了这两者,我们期望今年的 16.x 的某个版本中,支持可以选择性的使用它们。

Data Fetching 的进展

虽然 React 没有规定你如何请求数据,但是第一版用来请求数据的 Suspense 很有可能专注于集成固定的数据请求库。比如,在 Facebook,我们正在使用即将发布的集成了 Suspense 的 Relay APIs。我们也将会给出对其他库(比如 Apollo)做类似的整合的文档。

在第一个版本中,我们并没有打算专注于我们在早期演示中(也称为“React Cache”)使用的临时 “触发HTTP请求” 解决方案。但是,我们希望我们和 React 社区将在首次发布后的几个月内仍会在这个领域继续探索。

服务端渲染的进展

我们已经开始研究新的支持 Suspense 的服务器端渲染引擎,但是我们不认为它在并发模式的最初版本将会就绪。然而,这个版本将会提供一个临时的解决方案,这个方案可以让现有的服务端渲染引擎在 Suspense 的回调函数中立即生成 HTML,然后在客户端渲染出真正的内容。这就是我们在流式渲染引擎就绪之前,当前在 Facebook 内部使用的方案。

为什么要花费这么久时间?

随着每个单独的部件趋于稳定,我们将它们迁移到了并发模式中,其中包括了 新的 context API含有 Suspense 的懒加载 以及 Hook。我们也十分期望发布其他缺失的部分,但是大规模尝试它们是这个过程中一个很重要的部分。坦诚地说,我们比最开始预想投入了更多的工作。我们会一如既往,感谢你在 Twitter问题跟踪 中提出的问题和反馈。

安装

React

npm registry 中提供了 React v16.9.0。

使用 yarn 安装 React 16,执行如下命令:

yarn add react@^16.9.0 react-dom@^16.9.0

使用 npm 安装 React 16,执行如下命令:

npm install --save react@^16.9.0 react-dom@^16.9.0

我们还通过 CDN 提供了 React 的 UMD 版本:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

请参阅详细安装说明文档

Changelog

React

  • 提供 <React.Profiler> API 实现以编程的方式进行性能评估。(@bvaughn in #15172)
  • 删除 unstable_ConcurrentMode 以支持 unstable_createRoot。(@acdlite in #15532)

React DOM

React DOM Server

  • 修复 camelCase 自定义 CSS 属性名称的错误输出。(@bedakb in #16167)

React Test Utilities and Test Renderer