出品 | 技术栈(微信号:blog51cto)
React的一个改动细节惹起了前端圈的声讨。
“这项改动违反了React的第一设计准则!”
“这会清楚降低经常使用React框架的网站的性能。”
“这是一个令人恼火、无法了解的改动!”
提起React,作为环球上最受欢迎和经常使用最多的UI框架,许多网络“大牌”都有它的身影:不用提新潮一代的Netflix、Airbnb、Discord 等,仅降生地Meta(Facebook、Instagram 和 Whatsapp)就曾经足够让其扬名天下。
思索到 React 用于创立数十亿人经常使用的用户界面,这样说也不为过:互联网流量的很大一局部是由 React “处置”的。
一、一场探讨让React官网暂停了名目
这里的关键话题,正在探讨的变化正是React19版本。
随着React 19全新配置和 DX 改良,处置并行fetching疑问的方式出现了变化:禁用了同一 Suspense 边界内兄弟节点的并行渲染。这实质上为在这些兄弟节点外部失掉的数据引入了数据失掉瀑布。
这个小变化后来并没有惹起人们留意,却被被一位细心的前端大牛Dominik发现了发到海外社交媒体上,并示意这或许会清楚降低许多依赖 React 的网站的性能,一时期惹起了网友们的鼎沸探讨。
一切都从Dominik发的这条推文开局:
帖子中指出:React19查问如今是以瀑布方式运转。
如今,关于那些不意识 Dominik(又名TkDodo)的人来说,他与传奇的Tanner Linsley一同是宽泛经常使用的TanStack Query的**保养者之一。
一切都从这条推文开局:
Dominik(又名TkDodo)何许人?他与传奇的Tanner Linsley一同是宽泛经常使用的TanStack Query的**保养者之一。
帖子中指出:React19在Suspense处置并行fetching的方式跟React18不一样了?查问如今是以瀑布方式运转。
随后惹起了连锁反响:
1.“这是一个令人恼火,无法了解的改动!不只客户端组件中也变化了,甚至RSC中的并行fetch也变了。这对react-query而言是一场劫难。”
2.“可想而知,这也会影响lazily加载的组件(react.lazy)!”
3.我能了解Dominik的痛苦:这难道不违反React第一设计准则:compositon吗?假设我了解正确的话,如今你无法再经常使用复杂的预取技巧来编写数据需求。或许你必定将一切数据提取优化到公共父级。
此次React 19禁用了同一 Suspense 边界内兄弟节点的并行渲染,这实质上为在这些兄弟节点外部失掉的数据引入了数据失掉瀑布。
以下是此类事情的一个例子:
一切这一切中最蹩脚的是,除了性能会有重大降低,而且还将影响很多依赖这种形式的人,但只要一行名目符号毫不客气地提到了这一变化。
很多人兴许疑惑:终究是怎样回事?上方咱们就细细道来。
二、悬疑回忆
为了了解这究竟是什么,咱们首先须要极速回忆一下 React 的 Suspense。
Suspense 是一个 React 组件,它准许您显示一个回退,直到其子组件成功加载,要么是由于这些子组件是提前加载的,要么是由于它们正在经常使用启用 Suspense 的数据失掉机制。
经常使用方式如下:
虽然 Suspense 曾经成为 React API 的一局部很长一段时期了,然而在很长一段时期内,它的惟一官网同意用途是经常使用来提前加载组件React.lazy,这关于对运行程序启动代码宰割并仅在须要时加载宰割的局部十分有用。
当与 一同经常使用时,当第一次性尝试渲染提前加载的组件时(即,在提前加载之前),它会触发 Suspense 边界(即包装组件)并渲染回退,直到失掉组件的代码成功,而后它会渲染组件React.lazySuspense自身。
常年以来,咱们不时被承诺在客户端上为 Suspense 提供官网数据提取支持(经常使用 RSC 时,它曾经在主机上运转),但直到如今咱们才真正成功它,虽然如此,许多库(TanStack Query 就是其中之一)曾经经过钻研 React 的外部结构成功了它。正由于如此,目前消费中有许多运行程序确实经常使用Suspense在客户端上启动数据提取。
三、性能变慢了?
从如今起(React 18.3.1),当在同一个 Suspense 边界内经常使用启用 Suspense 的数据失掉或提前加载多个组件时,React 会在分开渲染之前尝试渲染一切兄弟组件,即使第一个兄弟组件已暂停。
实践上,这象征着这些兄弟节点中出现的数据失掉或提前加载将所有并行启动。
上方是一个展现这个想法的例子:
App { Suspense fallback{}ComponentThatFetchesData val{} ComponentThatFetchesData val{} ComponentThatFetchesData val{} Suspense}const ComponentThatFetchesData { val } {const result fetchSomethingSuspenseval {result}}
演示地址:
在这个例子中(在 React 18 中),即使fetchSomethingSuspense造成第一个ComponentThatFetchesData暂停,React 依然会尝试渲染它的兄弟,这将触发每个兄弟的并行数据失掉。
经过检查每次触发数据提取时记载的控制台,可以看出:一切数据失掉简直同时开局。
如今让咱们看看当咱们在 React 19(canary)中运转齐全相反的代码时会出现什么:
演示地址:
当咱们再次检查控制台时,咱们留意到如今有一个瀑布,由于每个数据提取仅在前一个数据成功后才启动。
出现这种状况是由于以下 PR:
在该 PR 引入更改之后,React 不会尝试渲染同一 Suspense 边界内的一切兄弟组件,而是会在第一个挂起的组件上丢弃,在这种状况下,您会首先尝试渲染第一个组件,而后它会挂起,而后只要在其数据失掉成功并且您可以渲染它之后,您才会抵达下一个兄弟组件,它将再次挂起,依此类推每个兄弟组件。
此外,这种新行为不只会影响经常使用 Suspense 启动数据提取的经常使用,还会影响经常使用React.lazy,由于这是一种旧形式,所以失掉了官网支持并且愈加宽泛。
四、面前是什么鬼
这一变化面前的理由(在前面提到的 PR 中写明)是,在实践暂停之前尝试渲染一切兄弟节点元素消耗,并且会提前显示回退。
此外,这一变化与React 团队自 React 18 之前引入 Suspense 以来不时在推进的“失掉时渲染”方法相反相成。
理想状况下,咱们不应该在经常使用它的同一组件的渲染上启动数据提取,而应该将其优化并尽早开局提取数据。
虽然从性能角度来看这无疑是最好的方法,但它确实带来了清楚的 DX 缺点,由于无法共置组件及其数据要求。
这里不会深化探讨这个话题,由于曾经有很多人探讨了它,甚至有一个专门为处置这个疑问而创立的库,但这里介绍某条特定观念的推文:
这里的关键内容是,假设不经常使用编译器,就无法能同时领有最佳的性能个性以及组件及其数据需求的搭配,而这正是 Relay 所做的。
五、终局还算圆满
幸运的是,这个故事有一个圆满的终局。在教训了少量群众推戴、强烈探讨以及幕后争执之后,React 团队最终选择临时推延这一扭转。
这并不是社区第一次性推戴在不思索 React 在 Meta 和 Vercel 之外的经常使用方式的状况下引入的更改。React 团队(尤其是 Vercel)推进将 RSC 成为经常使用 React 构建的基本组成局部就是一个例子。
很清楚,React 保养者和社区关于 React 的未来前景的看法存在对抗。这些疑问沟通会有哪些停顿?后续还有待观察。