当前位置: 首页 > 科技观察

ReactEffectsList的大重构是为了他?

时间:2023-03-13 12:24:05 科技观察

大家好,我是Kason。在本文中,我们将了解重构React内部的EffectsList机制的前因后果。阅读本文后,您可以掌握React18中Suspense特性与之前版本的差异和原因。什么是副作用React的简单工作原理可以概括为:1.触发更新2.渲染阶段:计算更新带来的副作用3.提交阶段:执行副作用副作用包括很多种,例如:Placement是指DOM节点的插入和移动Passive是指useEffect回调执行ChildDeletion是指移除子DOM节点等引起DOM变化的更新,主要是Placement和ChildDeletion在起作用。那么render阶段如何保存sideeffects,commit阶段如何使用sideeffects呢?EffectsList重构之前,在render阶段,会把有sideeffects的节点连接起来形成一个链表。该链表称为效果列表。比如下图中,B、C、E有副作用,它们连接起来形成一个EffectsList:commit阶段不需要从A向下遍历整棵树,只需要遍历Effects列表找出所有有副作用的节点,并进行相应的操作。SubtreeFlags重构后,子节点的副作用会冒泡到父节点的SubtreeFlags属性中。例如B、C、E中包含的sideeffects如下图所示:冒泡过程如下:B的sideeffect为Passive,向A冒泡,A.SubtreeFlags包含Passive。E的副作用是Placement,它冒泡到D,D.SubtreeFlags包含PlacementD冒泡到C,C.SubtreeFlags包含Placement,C的副作用是Update,C.SubtreeFlags包含Placement,C冒泡到A,和最后A.SubtreeFlags包含Passive、Placement、Update,也就是说A的子树包含了这三个sideeffects。在commit阶段,根据SubtreeFlags,逐层寻找有副作用的节点,并进行相应的操作。可以看出,SubtreeFlags需要遍历树,而EffectsList只需要遍历链表,效率更高。那么为什么要重构React呢?Suspense的回答是:SubtreeFlags需要遍历比EffectsList更多的节点来遍历子树,但是React18中的一个新特性只是需要“遍历子树”。此功能是悬念。Suspense是v16中提供的功能,但是在v18之后,开启并发功能后,Suspense的行为与之前的版本有所不同。考虑以下组件:loading...}>其中LazyCpn是用React.lazy包装的异步加载组件。Sibling代码如下:functionSibling(){useEffect(()=>{console.log("Siblingeffect");},[]);return

Sibling

;}因为Suspense会等待异步在子组件请求完成后Render,所以代码运行时,页面会先渲染fallback:

loading...

但是Sibling不是异步的!下面是React新旧版本的区别。React新老版本的区别先回顾一下开头介绍的简单React的工作原理:触发更新Render阶段:协调器计算更新带来的副作用Commit阶段:渲染器执行副作用在启用并发之前,React保证渲染阶段对应于提交阶段。所以在上面的例子中,虽然LazyCpn请求的是Suspense渲染回退,但是并不会阻止Sibling的渲染,也不会阻止Sibling中useEffect的执行。控制台仍会打印“Siblingeffect”。同时,为了直观的显示Sibling没有被渲染,Sibling渲染的DOM节点会被设置为display:none:但这其实是相当hack的。毕竟按照Suspense的理念,如果子孙组件有异步加载的内容,应该只渲染fallback(而不是同时渲染display:none的内容)。因此,新版本在Suspense中单独做了一个“不显示子树”处理,既不会渲染display:none的内容,也不会执行useEffect回调:实现这部分处理的基础是改变commit中的遍历方式phase,返回开头提到的EffectsList,重构为subtreeFlags。从这个线上Demo可以直观的感受到新旧版本Suspense的区别[1]总结今天我们又学习了一个关于React源码的小知识。值得一提的是,本次对Suspense的改进为React带来了一种新型的内部组件——OffscreenComponent。以后他可能是实现react版keep-alive的基础。参考[1]在线Demo:https://codesandbox.io/s/frosty-currying-35olk?file=/src/App.js