在rootFiber.firstEffect
上保存了一条需要执行副作用
的Fiber节点
的单向链表effectList
,这些Fiber节点
的updateQueue
中保存了变化的props
。
这些副作用
对应的DOM操作
在commit
阶段执行。
除此之外,一些生命周期钩子(比如componentDidXXX
)、hook
(比如useEffect
)需要在commit
阶段执行。
阶段的主要工作(即Renderer
的工作流程)分为三部分:
mutation阶段(执行
DOM
操作)
你可以从这里 (opens new window)看到commit
阶段的完整代码
这些对我们当前属于超纲内容,为了内容完整性,在这节简单介绍。
commitRootImpl
方法中直到第一句if (firstEffect !== null)
之前属于before mutation
之前。
我们大体看下他做的工作,现在你还不需要理解他们:
可以看到,before mutation
之前主要做一些变量赋值,状态重置的工作。
这一长串代码我们只需要关注最后赋值的firstEffect
,在commit
的三个子阶段都会用到他。
layout之后
接下来让我们简单看下阶段执行完后的代码,现在你还不需要理解他们:
主要包括三点内容:
useEffect
相关的处理。
源码里有很多和interaction
相关的变量。他们都和追踪React
渲染时间、性能相关,在和DevTools (opens new window)中使用。
你可以在这里看到
- 在
commit
阶段会触发一些生命周期钩子(如componentDidXXX
)和hook
(如useLayoutEffect
、useEffect
)。
在这些回调方法中可能触发新的更新,新的更新会开启新的render-commit
流程。考虑如下Demo:
useLayoutEffect Demo
在该Demo中我们点击页面中的数字,状态会先变为0,再在useLayoutEffect
回调中变为随机数。但在页面上数字不会变为0,而是直接变为新的随机数。
这是因为useLayoutEffect
会在layout阶段
同步执行回调。回调中我们触发了状态更新setCount(randomNum)
,这会重新调度一个同步任务。
该任务会在在如上commitRoot
倒数第二行代码处被同步执行。
所以我们看不到页面中元素先变为0。
关注公众号,后台回复908获得在线Demo地址