课程地址 https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b360cf10a4003b634722
在 render阶段
的末尾会调用 commitRoot(root);
进入 commit阶段
,这里的 root
指的就是 fiberRoot
,然后会遍历 render阶段生成的effectList
,effectList
上的 Fiber节点
保存着对应的 props变化
,进行对应的 dom操作
和 生命周期
、hooks回调或销毁函数
,各个函数做的事情如下
在 commitRoot
函数中其实是调度了 commitRootImpl
函数
在 commitRootImpl
的函数中主要分三个部分:
commit阶段前置工作
- 调用
flushPassiveEffects
执行完所有effect
的任务 - 初始化相关变量
- 赋值
firstEffect
给后面遍历effectList
用
mutation
遍历 effectList
分别执行三个方法 commitBeforeMutationEffects
、commitMutationEffects
、commitLayoutEffects
执行对应的dom操作和生命周期
在介绍 双缓存Fiber树
的时候,我们在构建完 workInProgress Fiber树
之后会将 fiberRoot
的 current
指向 workInProgress Fiber
,让 workInProgress Fiber
成为 current
,这个步骤发生在 commitMutationEffects
函数执行之后,commitLayoutEffects
之前,因为 componentWillUnmount
发生在 commitMutationEffects
函数中,这时还可以获取 之前的Update
,而 componentDidMount
和 componentDidUpdate
会在 commitLayoutEffects
中执行,这时已经可以获取更新后的真实dom了
mutation 后
- 根据
rootDoesHavePassiveEffects
赋值相关变量 - 执行
flushSyncCallbackQueue
处理componentDidMount
等生命周期或者useLayoutEffect
等同步任务
commitBeforeMutationEffects主要做了两件事
- 执行
getSnapshotBeforeUpdate
在源码中commitBeforeMutationEffectOnFiber
对应的函数是commitBeforeMutationLifeCycles
在该函数中会调用getSnapshotBeforeUpdate
,现在我们知道了getSnapshotBeforeUpdate
是在mutation阶段
中的commitBeforeMutationEffect
函数中执行的,而commit阶段
是同步的,所以getSnapshotBeforeUpdate
也同步执行 - 调度
useEffect
。 在flushPassiveEffects
函数中调用flushPassiveEffectsImpl
遍历pendingPassiveHookEffectsUnmount
和pendingPassiveHookEffectsMount
,执行对应的effect回调和销毁函数
,而这两个数组是在commitLayoutEffects
函数中赋值的(待会就会讲到),mutation后
effectList
赋值给rootWithPendingPassiveEffects
,然后scheduleCallback
调度执行flushPassiveEffects
。 注意,和在render阶段
的fiber node
会打上Placement
等标签一样,useEffect
或useLayoutEffect
也有对应的effect Tag
,在源码中对应export const Passive = /* */ 0b0000000001000000000;
commitMutationEffects主要做了如下几件事
- 调用
commitDetachRef
解绑ref - 根据
effectTag
执行对应的dom操作 useLayoutEffect
销毁函数在UpdateTag
时执行
现在让我们来看看操作dom的这几个函数
commitPlacement
插入节点:找到该节点最近的parent节点和兄弟节点
,然后根据isContainer
来判断是插入到兄弟节点前
还是append到parent节点后
commitWork
更新节点: 如果fiber
的tag
是SimpleMemoComponent
则会调用commitHookEffectListUnmount
执行对应的hook
的销毁函数,可以看到传入的参数是HookLayout | HookHasEffect
,也就是说执行useLayoutEffect的销毁函数
。如果是HostComponent
,那么调用commitUpdate
,commitUpdate
最后会调用updateDOMProperties
处理对应Update
的dom操作commitDeletion
删除节点: 如果是ClassComponent
会执行componentWillUnmount
,删除fiber
,如果是FunctionComponent
会删除ref
、并执行useEffect
的销毁函数,具体可在源码中查看unmountHostComponents
、commitNestedUnmounts
、detachFiberMutation
这几个函数
commitLayoutEffects在 commitMutationEffects
之后所有的dom操作都已经完成,可以访问dom了
- 调用
commitLayoutEffectOnFiber
执行相关生命周期函数
或者hook相关callback
。 在源码中commitLayoutEffectOnFiber
函数的别名是commitLifeCycles
,commitLifeCycles会判断fiber的类型,SimpleMemoComponent会执行useLayoutEffect的回调,然后调度useEffect,ClassComponent会执行componentDidMount或者componentDidUpdate,this.setState第二个参数也会执行,HostRoot会执行ReactDOM.render函数的第三个参数 - 执行
commitAttachRef
为ref
赋值