警告:本文档介绍的实验功能在稳定版本中尚不可用。不要在生产应用程序中依赖 React 的实验性构建。这些功能可能会发生重大更改,而且直到功能成为 React 的一部分之前这类更改都不会发出警告。本文档面向早期使用者和对此感兴趣的用户。如果你不熟悉 React,那就不必为这些功能担心——它们并非你现在就需要学习的内容。本文档提供了并发模式(Concurrent Mode)的理论概述。如果需要更具体的介绍,可以查阅文末的附录。
什么是并发模式?
并发模式是一组新功能,可以帮助 React 应用程序保持响应状态,并适当调整用户的设备功能和网络速度。
这些功能仍处于实验阶段,未来可能会发生更改。它们还不是稳定的 React 版本的一部分,但你可以在实验版本中尝试它们。
阻塞与可中断渲染
为了更好地解释并发模式,我们用版本控制来打比方。如果你在一个团队中工作,你们可能会使用像 Git 这样的版本控制系统,开发很多分支。当某个分支完成后,你可以将你的工作合并到主干上,这样其他人就可以拉取它了。
版本控制系统还没诞生的时候,开发工作流程是完全不一样的——那时没有分支的概念,如果要编辑某些文件,必须告诉所有人在你完成工作之前不要碰这些文件。你甚至无法与别人同时研究它们——实际上这些文件把你圈住了。
这就是今天包括 React 在内的 UI 库常见的工作机制。一旦它们开始渲染一项更新,或者创建新的 DOM 节点,并在组件内部运行代码等,它们就不会中断这项工作。我们将这种方法称为“阻止渲染”。
在并发模式下,渲染不会阻塞——它是可中断的,这改善了用户体验。它还可以解锁以前无法实现的一些新功能。这篇文档就是对这一新功能的高层次概述。
可中断渲染
考虑一个可过滤的产品列表。你可能遇到过这种情况,那就是在列表过滤器中输入过滤条件时,每次按键都会出现卡顿。更新产品列表时,某些工作可能是不可避免的,例如创建新的 DOM 节点或由浏览器绘制布局等。但是,我们 何时 以及 如何 执行这些任务是问题的关键所在。
解决卡顿的一种常见方法是对输入“消除抖动”。启用防抖功能后,我们只会在用户停止输入 之后 才更新列表。但我们敲键盘时,UI 是不会更新的,这很让人泄气。或者我们可以“限流”输入,并以某个给定的频率上限来更新列表。但在性能较低的设备上我们仍然会卡顿。防抖和限流都会带来不够理想的用户体验。
卡顿的原因很简单:渲染开始后就不能中断了,因此浏览器无法在键盘按下后立即更新文本输入。无论 UI 库(例如 React)在基准测试上表现多好,如果它使用阻塞渲染,那么总会有组件中的某些工作造成卡顿,而且通常没有简单的解决方案。
并发模式使渲染可中断,从而从根本上解除了这一限制。这意味着当用户按下一个键时,React 不需要阻止浏览器更新文本输入。相反,它可以让浏览器绘制输入的更新,然后继续 在内存中 渲染更新后的列表。渲染完成后,React 将更新 DOM,更改将反映在屏幕上。
从概念上讲,你可以将其视为 React 在“分支”上准备每个更新的过程。就像你可以放弃分支工作或在分支之间切换一样,“并发模式”下的 React 可以中断正在进行的更新以执行更重要的任务,然后返回到之前的工作。这种技术可能会让你想起视频游戏中的双重缓冲。
并发模式技术减少了 UI 中的防抖和限流的需求。由于渲染是可中断的,因此 React 无需人为地 延迟 工作来避免卡顿。它可以立即开始渲染,但在需要使应用程序保持响应时会中断这项工作。
指定加载顺序
前文提到,并发模式就好像 React 使用“分支”工作一样。分支不仅对短期修复有用,而且对长期运行的功能也有意义。有时你可能会开发某项功能,但可能要花几周的时间才能使其处于“足够好的状态”以合并入主干。和版本控制相似,渲染也是一个道理。
想象一下,我们正在一个应用程序的两个页面之间跳转。有时,我们可能没加载足够的代码和数据来在新页面上向用户显示“足够好”的加载状态。跳转到空白页或显示一个巨大的处理中的图标是非常糟心的体验。但是,获取必要的代码和数据往往也不会花费太长时间。如果 React 可以在旧页面上停留更长的时间,并在显示新页面之前“跳过”“不良加载状态”,效果不就会更好了吗?
尽管现在这是可以做到的,但具体执行起来很麻烦。在并发模式下,这一功能是内置的。React 首先开始在内存中准备新页面,或者正如我们比喻的情况,“在另一个分支上”做准备。因此 React 可以在更新 DOM 之前等待加载更多内容。在并发模式下,我们可以告诉 React 继续显示可完全互动的旧页面,并在页面上嵌入加载指示器。当新页面准备就绪时,React 就可以带我们跳转过去。
并 发
回顾一下上面的两个例子,看看并发模式如何将它们结合起来。在并发模式下,React 可以并行处理多个状态更新——就像不同的团队成员使用分支独立工作一样:
- 对于受 CPU 影响的更新(例如创建 DOM 节点和运行组件代码),并发意味着更紧急的更新可以“中断”已经开始的渲染。
- 对于受 IO 影响的更新(例如从网络中获取代码或数据),并发意味着 React 甚至可以在所有数据到达之前就开始在内存中渲染,无需显示令人讨厌的加载中状态。
重点在于,你使用React 的方式没有变化。组件、props 和状态之类的概念本质上没有改变。要更新屏幕时,你就会设置状态。
React 使用启发式方法来决定更新的“紧急程度”,并允许你使用少量几行代码来调整,以便在每次交互时都获得所需的用户体验。
将研究成果投入生产
这些并发模式功能有一个共同的目标。它的任务是帮助将人机交互研究的成果整合到现实的用户界面中。
例如,研究表明,在页面之间切换时显示过多的中间加载状态会 拖慢 切换速度,所以并发模式会使用固定的“时间表”显示新的加载状态,以避免卡顿和过于频繁的更新。
同样,从研究中我们知道,悬停和文本输入之类的交互需要在很短的时间内处理完毕,而单击和页面跳转在让用户感到卡顿之前可以等待更长的时间。并发模式在内部使用的不同“优先级”大致对应于人类感知研究中的交互类别。
专注于用户体验的团队有时会通过一次性解决方案来处理类似的问题。但这些解决方案很难维护,因此很难长久。并发模式的目标是将 UI 研究的成果纳入抽象本身,并提供符合习惯的方式来使用它们。作为一个 UI 库,React 可以很好地做到这一点。
下一步
现在你知道了什么是并发模式!
下面的这些文档提供了有关特定主题的更多详细信息:
- 为数据获取挂起——描述了一种在 React 组件中获取数据的新机制。https://reactjs.org/docs/concurrent-mode-suspense.html
- 并发用户界面模式——展示了一些基于并发模式和挂起模式的用户界面模式。https://reactjs.org/docs/concurrent-mode-patterns.html
- 采用并发模式——说明了如何在项目中尝试并发模式。https://reactjs.org/docs/concurrent-mode-adoption.html
- 并发模式 API 参考——实验版本中可用的新 API 文档。https://reactjs.org/docs/concurrent-mode-reference.html
原文链接:
Introducing Concurrent Mode (Experimental) – Reactreactjs.org