React v17.0
今天,我们宣布 React 17 正式发布!在此之前,我们在 React 17 RC 的博文中已经介绍了 React 17 发布的意义以及包含的变化。此文是针对那篇文章的简单总结,如果你已阅读过那篇博文,此文可略过。
无新特性
React v17 的发布非比寻常,因为它没有增加任何面向开发者的新特性。但是,这个版本会使得 React 自身的升级变得更加容易。
值得特别说明地是,React v17 作为后续版本的 ”基石“,它让不同版本的 React 相互嵌套变得更加容易。
除此之外,还会使 React 更容易嵌入到由其他技术构建的应用中。
渐进式升级
React v17 开启了 React 渐进式升级的新篇章。当你从 React 15 升级至 16 时(或者,从 16 升级到 17),你通常会一次性升级整个应用程序,这对大部分应用来说十分有效。但是,如果代码库编写于几年前,并且没有及时的维护升级,这会使得升级成本越来越高。并且,在 React 17 之前,如果在同一个页面上使用不同的 React 版本(可以这么做,但是有风险),会导致事件问题的出现,会有一些未知的风险。
我们正在修复 React v17 中的许多问题。这意味着,当 React 18 或未来版本来临时,你将有更多选择。首选,当然还是一次性升级整个应用;但你还有个可选方案,渐进式升级你的应用。举个例子,你可能将大部分功能升级至 React v18,但保留部分懒加载的对话框或子路由在 React v17。
但这并不意味着你必须进行渐进式升级。对于大多数应用来说,一次性升级仍是更好的选择。加载两个版本的 React,仍然不是理想方案 —— 即使其中一个版本是按需加载的。但对于那些长期未维护的大型应用来说,这意义非凡,React v17 开始让这些应用不会被轻易淘汰。
我们准备了示例仓库,此示例演示了如何在必要时懒加载旧版本的 React。此示例由 Create React App 构建,使用其他工具也可以实现同样的效果。欢迎使用其他工具的小伙伴通过 PR 的形式提供 Demo。
注意
我们将其他特性推迟到了 React v17 之后。这个版本的目标就是实现渐进式升级。如果升级到 17 很困难,那就违背了此版本的目的。
事件委托的变更
为了实现渐进式升级,我们需要对 React 的事件系统进行修改。React 17 是一个重要版本,因为这个版本的可能存在破坏性更改。关于版本的更多信息,请查阅版本的 FAQ,以了解我们对版本稳定性的承诺。
React v17 中,React 不会再将事件处理添加到 document
上,而是将事件处理添加到渲染 React 树的根 DOM 容器中:
const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
在 React 16 及之前版本中,React 会对大多数事件进行 document.addEventListener()
操作。React v17 开始会通过调用 rootNode.addEventListener()
来代替。
经核实,多年来在 issue 追踪器 上报告的许多问题都已被新特性解决,其中大多与将 React 与非 React 代码集成有关。
如果你在升级时遇到了这方面的问题,可以看看这个常见的解决方案。
其他破坏性更改
React v17 的 RC 博文描述了关于 React v17 中其他的破坏性更改。
我们在升级 Facebook 项目代码中 10w+ 组件的过程中,只修改了不到 20 个组件,所以我们猜测大多数应用在升级 v17 时,不会有太大的问题。如果你遇到任何问题,请告诉我们。
全新的 JSX 转换
React v17 支持了全新的 JSX 转换。我们还针对 React 16.14.0,React 15.7.0 和 0.14.0 版本做了兼容。请注意,此功能完全可选,并非必须使用。之前的 JSX 转换将会继续维护,并且没有停止支持它的计划。
React Native
React Native 会有一个单独的发布计划。目前,我们对 React v17 的支持已在 React Native 0.64 中落地。你可以在 React Native 社区的发布 issue tracker 上参与讨论。
安装
使用 npm 安装 React v17:
npm install react@17.0.0 react-dom@17.0.0
使用 yarn 安装 React v17:
yarn add react@17.0.0 react-dom@17.0.0
我们还提供了由 UMD 构建的 CDN 版本:
<script crossorigin src="https://unpkg.com/react@17.0.0/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17.0.0/umd/react-dom.production.min.js"></script>
请参阅文档中的详细安装说明。
变更日志
React
- 为全新的 JSX 转换器添加
react/jsx-runtime
和react/jsx-dev-runtime
。(@lunaruan 提交于 #18299) - 根据原生框架构建组件调用栈。(@sebmarkbage 提交于 #18561)
- 可以在 context 中设置
displayName
以改善调用栈信息。(@eps1lon 提交于 #18224) - 防止
'use strict'
从 UMD 的 bundles 中泄露。(@koba04 提交于 #19614) - 停止使用
fb.me
进行重定向。(@cylim 提交于 #19598)
React DOM
- 将事件委托从
document
切换为 root。(@trueadm 提交于 #18195 及其他) - 在运行下一个副作用前,请清理所有副作用。(@bvaughn 提交于 #17947)
- 异步运行
useEffect
清理函数。(@bvaughn 提交于 #17925) - 使用浏览器的
focusin
和focusout
替换onFocus
和onBlur
的底层实现。(@trueadm 提交于 #19186) - 将所有
Capture
事件都使用浏览器的捕获阶段实现。(@trueadm 提交于 #19221) - 禁止在
onScroll
事件时冒泡。(@gaearon 提交于 #19464) - 如果
forwardRef
或memo
组件的返回值为undefined
,则抛出异常。(@gaearon 提交于 #19550) - 移除事件池。(@trueadm 提交于 #18969)
- 移除 React Native Web 不需要的内部组件。(@necolas 提交于 #18483)
- 当挂载 root 时,附加所有已知的事件监听器。(@gaearon 提交于 #19659)
- 在 Dev 模式下,禁用第二次渲染过程中的
console
。(@sebmarkbage 提交于 #18547) - 弃用为记录且具有误导性的
ReactTestUtils.SimulateNative
API。(@gaearon 提交于 #13407) - 重命名内部使用的私有字段(@gaearon 提交于 #18377)
- 不在开发环境调用 User Timing API。(@gaearon 提交于 #18417)
- 在严格模式下重复渲染期间禁用 console。(@sebmarkbage 提交于 #18547)
- 在严格模式下,二次渲染组件也不使用 Hook。(@eps1lon 提交于 #18430)
- 允许在生命周期函数中调用
ReactDOM.flushSync
(但会发出警告)。(@sebmarkbage 提交于 #18759) - 将
code
属性添加到键盘事件对象中。(@bl00mber 提交于 #18287) - 为
video
元素添加disableRemotePlayback
属性。(@tombrowndev 提交于 #18619) - 为
input
元素添加enterKeyHint
属性。(@eps1lon 提交于 #18634) - 当没有给
<Context.Provider>
提供任何值时,会发出警告。(@charlie1404 提交于 #19054) - 如果
forwardRef
或memo
组件的返回值为undefined
,则抛出警告。(@bvaughn 提交于 #19550) - 为无效更新改进错误信息。(@JoviDeCroock 提交于 #18316)
- 从调用栈信息中忽略 forwardRef 和 memo。(@sebmarkbage 提交于 #18559)
- 在受控输入与非受控输入间切换时,改善错误消息。(@vcarl 提交于 #17070)
- 保持
onTouchStart
、onTouchMove
和onWheel
默认为 passive。(@gaearon 提交于 #19654) - 修复在 development 模式下 iframe 关闭时,
setState
挂起的问题。(@gaearon 提交于 #19220) - 使用
defaultProps
修复拉架子组件在渲染时的问题。(@jddxf 提交于 #18539) - 修复当
dangerouslySetInnerHTML
为undefined
时,误报警告的问题。(@eps1lon 提交于 #18676) - 使用非标准的
require
实现来修复 Test Utils。(@just-boris 提交于 #18632) - 修复
onBeforeInput
报告错误的event.type
。(@eps1lon 提交于 #19561) - 修复 Firefox 中
event.relatedTarget
输出为undefined
的问题。(@claytercek 提交于 #19607) - 修复 IE11 中 “unspecified error” 的问题。(@hemakshis 提交于 #19664)
- 修复 shadow root 中的渲染问题。(@Jack-Works 提交于 #15894)
- 使用事件捕获修复
movementX/Y
polyfill 的问题。(@gaearon 提交于 #19672) - 使用委托处理
onSubmit
和onReset
事件。(@gaearon 提交于 #19333) - 提高内存使用率。(@trueadm 提交于 #18970)
React DOM Server
- 使用服务端渲染的
useCallback
与useMemo
一致。(@alexmckenley提交于 #18783) - 修复函数组件抛出异常时状态泄露的问题。(@pmaccart 提交于 #19212)
React Test Renderer
- 改善
findByType
错误信息。(@henryqdineen 提交于 #17439)
Concurrent Mode(实验阶段)
- 改进启发式更新算法。(@acdlite 提交于 #18796)
- 在实验性 API 前添加
unstable_
前缀。 (@acdlite 提交于 #18825) - 移除
unstable_discreteUpdates
和unstable_flushDiscreteUpdates
。(@trueadm 提交于 #18825) - 移除了
timeoutMs
参数。(@acdlite 提交于 #19703) - 禁用
<div hidden />
预渲染,以支持未来的 API。(@acdlite 提交于 #18917) - 为 Suspense 添加了
unstable_expectedLoadTime
,用于 CPU-bound 树。(@acdlite 提交于 #19936) - 添加了一个实验性的
unstable_useOpaqueIdentifier
Hook。(@lunaruan 提交于 #17322) - 添加了一个实验性的
unstable_startTransition
API. (@rickhanlonii 提交于 #19696) - 在测试渲染器中使用
act
后,不在刷新 Suspense 的 fallback。(@acdlite 提交于 #18596) - 将全局渲染的 timeout 用于 CPU Suspense。(@sebmarkbage 提交于 #19643)
- 挂载前,清除现有根目录的内容。(@bvaughn 提交于 #18730)
- 修复带有错误边界的 bug。(@acdlite 提交于 #18265)
- 修复了导致挂起树更新丢失的 bug。(@acdlite 提交于 #18384 以及 #18457)
- 修复导致渲染阶段更新丢失的 bug。(@acdlite 提交于 #18537)
- 修复 SuspenseList 的 bug。(@sebmarkbage 提交于 #18412)
- 修复导致 Suspense fallback 过早显示的 bug。(@acdlite 提交于 #18411)
- 修复 SuspenseList 中使用 class 组件异常的 bug。(@sebmarkbage 提交于 #18448)
- 修复输入内容可能被更新被丢弃的 bug。(@jddxf 提交于 #18515 以及 @acdlite 提交于 #18535)
- 修复暂挂 Suspense fallback 后卡住的错误。(@acdlite 提交于 #18663)
- 如果 hydrate 中,不要切断 SuspenseList 的尾部。(@sebmarkbage 提交于 #18854)
- 修复
useMutableSource
中的 bug,此 bug 可能在getSnapshot
更改时出现。(@bvaughn 提交于 #18297) - 修复
useMutableSource
令人恶心的 bug。(@bvaughn 提交于 #18912) - 如果外部渲染且提交之前调用
setState
,会发出警告。(@sebmarkbage 提交于 #18838)