Suspense 实现
- 在
beginWork
时,遇到Suspense
,默认是正常显示组件:创建一个OffScreenComponent
来表示,此时visible
为true
。 - 当加载到组件内部的
promise
时,由于第一次渲染,此时还未加载完成,会报错,抛出promise
。 handleError
捕捉到错误,首先开始throwException
:- 标记报错的
fiber.flags
为Incomplete
。 - 向上查找,找到第一个
Suspense
。 - 在
Suspense.updateQueue
中添加报错信息(也就是抛出的promise
)。 - 通过
attachPingListener
方法执行该promise
,并在then
回调里添加一个更新任务。 - 标记
Suspense fiber flags
为ShouldCapture
。
- 标记报错的
- 从报错的
fiber
开始,执行completeWork
:- 向上找到
ShouldCapture
的Suspense
,标记为DidCapture
。 - 查找过程中遇到的
fiber
,标记为Incomplete
。 - 返回最终的
fiber
:Suspense
或者HostRoot
(没找到Suspense
)。
- 向上找到
- 对找到的
Suspense
重新开始beginWork
。此时DidCapture
为true
,显示fallback
。Suspense
的child
指向OffScreenComponent
(内部包含主要的组件),OffScreenComponent
的sibling
指向fallbackChildFragment
(占位组件)。而真正进行渲染的是fallbackChildFragment
。 - 在
completeWork
的时候,对于Suspense fiber
,检测到已经从显示primary
(主组件)变为显示fallback
(占位组件)。如果此时updateQueue
存在,说明还有待更新的任务,标记fiber
为Update
。 - 在
commitWork
阶段,对于标记Update
的Suspense
,会调用attachSuspenseRetryListeners
将所有异步任务的then
添加更新任务,对应的lane
为retryLane
。(如果之前已经更新过了,那么不需要重复更新,这里只是作为保障) - 等到
promise
加载完成,重新开始更新,此时就能正常的显示primary
组件了。