React 在今天刚刚在官网发布了有关于 React 18 的计划更新(The Plan for React 18 – React Blog),同时发布了 Alpha 版本的 npm 包:
下载地址:https://reactjs.org/blog/2021/06/08/the-plan-for-react-18.html
最近虽然忙到没时间做视频,不过这么大的事还是值得来聊聊(趁着公司电脑强制要求升 Big Sur 不让不让用的间隙……)。本文并非逐字翻译,一如既往的会带不少个人想法,不代表公司观点(听说这句话其实说了并没什么卵用是吧 Oppo)。
React 18 正式引入了对并发模式/特性的「渐进升级」策略
之前[1]我就提到过 React 18 会把重点放在解决兼容性和如何做迁移的问题上。
自从 Concurrent Mode(并发模式,以下简称 CM)宣布以来,Core Team 成员每天在推上 Github 上「XXX is not CM-safe」吓死个人,使得整个社区都一直在担心未来的 React 是不是会为了上 CM 直接 breaking change,然后不兼容 CM 的代码必须要全部迁移过来才能用 CM("all-or-nothing" upgrade strategy)。
这次 18 的计划发布终于横扫了大家的担忧 —— 并发的引入将会是 opt-in 得,不用的话就没有 breaking changes,整体采用了渐进升级的策略(gradual adoption strategy)。
博文里基本没讲怎么渐进,只是说「升级到 18 几乎不需要任何改动」。其实大概上有两个地方做到了「渐进」:
React 团队对 "Concurrency opt-in" roots 的兼容性做了很多优化,如果不用 CM 特性的话,大概率能 just works。如博客所说「concurrent rendering will only be enabled for updates triggered by one of the new features.」—— 从此再无 CM,只有 Concurrent Features(并发特性)[2]
对于直接想让应用的某一部分「躺平」的可以用 legacy root。我之前在 VLOG 第四期[3]中就和大家提到过,17 对事件系统的修改的目的之一就是为了让你的 React 应用同时可以跑在不同「版本」上[4]。18 的实际做法[5]是引入了新的 Root API ReactDOM.createRoot 来与旧的 ReactDOM.render API 区分开来,你可以将整个 React 树分形成不同的 roots,用旧 API 的 legacy roots 会跑在「legacy mode 传统模式」上(相当于跑在 17 上),用新 API 的 roots 会跑在 "Concurrency opt-in" roots 下。
React 18 的其他新特性
更加激进的「自动 batching」,React 17 只在事件回调中 batch,React 18 则会对任何来源的 setState 做尽可能多的 batching。 如果你跟我一样是用类似 state monad 的 mental model 来思考 state 的话,你可能会以为 React 早就是这个行为了。对于 Hooks 来说你是没有办法拿到中间状态的 state 而 Class 可以拿 this.state。这也是我以前[6]说过的 class 对比 FC 的纯度问题。
新的startTransition 与 useDeferredValue API,本质上都是允许你将 UI 的一部分标记为「较低的更新优先级」。
Suspense SSR。你可能知道也不知道的是,完全用 React 重写的新 Facebook.com 是用 Hermes 做 SSR 来优化首屏渲染得,所以 SSR 的优化就成为了 React 团队的优先级之一啦……但是传统 SSR 的一个问题是,全量渲染话延迟太高了。而 CM + Suspence 就可以做到用 Suspence boundary 将应用分片,然后以此为单位做流式 SSR,是不是有重新发明 BigPipe 的感觉了?
StrictMode 在既 double-render 之后加入了 double-effect
可以看到,随着 CM 的逐渐落地,其「底层能力」的一面终于开始逐渐显著出来,越来越多的上层应用将会发布以及正在开发中(比如 Brian 提到的 Offscreen API 以及 RN Pre-render)等。