Skip to content

Latest commit

 

History

History
57 lines (34 loc) · 4.72 KB

File metadata and controls

57 lines (34 loc) · 4.72 KB

commit阶段

课程地址 https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b360cf10a4003b634722

render阶段 的末尾会调用 commitRoot(root); 进入 commit阶段 ,这里的 root 指的就是 fiberRoot ,然后会遍历 render阶段生成的effectListeffectList 上的 Fiber节点 保存着对应的 props变化 ,进行对应的 dom操作生命周期hooks回调或销毁函数,各个函数做的事情如下

commit阶段

commitRoot

commitRoot 函数中其实是调度了 commitRootImpl 函数

commitRootImpl 的函数中主要分三个部分:

commit阶段前置工作

  • 调用 flushPassiveEffects 执行完所有 effect 的任务
  • 初始化相关变量
  • 赋值 firstEffect 给后面遍历 effectList

mutation

​遍历 effectList 分别执行三个方法 commitBeforeMutationEffectscommitMutationEffectscommitLayoutEffects 执行对应的dom操作和生命周期

​在介绍 双缓存Fiber树 的时候,我们在构建完 workInProgress Fiber树 之后会将 fiberRootcurrent 指向 workInProgress Fiber,让 workInProgress Fiber 成为 current,这个步骤发生在 commitMutationEffects 函数执行之后,commitLayoutEffects 之前,因为 componentWillUnmount 发生在 commitMutationEffects 函数中,这时还可以获取 之前的Update ,而 componentDidMountcomponentDidUpdate 会在 commitLayoutEffects 中执行,这时已经可以获取更新后的真实dom了

mutation 后

  1. 根据 rootDoesHavePassiveEffects 赋值相关变量
  2. 执行 flushSyncCallbackQueue 处理 componentDidMount 等生命周期或者 useLayoutEffect 等同步任务

mutation阶段的三个函数分别做了什么事情

commitBeforeMutationEffects主要做了两件事

  1. 执行 getSnapshotBeforeUpdate 在源码中 commitBeforeMutationEffectOnFiber 对应的函数是 commitBeforeMutationLifeCycles 在该函数中会调用getSnapshotBeforeUpdate,现在我们知道了 getSnapshotBeforeUpdate 是在 mutation阶段 中的 commitBeforeMutationEffect 函数中执行的,而 commit阶段 是同步的,所以 getSnapshotBeforeUpdate 也同步执行
  2. 调度 useEffect。​ 在 flushPassiveEffects 函数中调用 flushPassiveEffectsImpl 遍历 pendingPassiveHookEffectsUnmountpendingPassiveHookEffectsMount,执行对应的 effect回调和销毁函数,而这两个数组是在 commitLayoutEffects 函数中赋值的(待会就会讲到),mutation后 effectList 赋值给 rootWithPendingPassiveEffects ,然后 scheduleCallback 调度执行 flushPassiveEffects 。 注意,和在 render阶段fiber node 会打上 Placement 等标签一样,useEffectuseLayoutEffect 也有对应的 effect Tag,在源码中对应 export const Passive = /* */ 0b0000000001000000000;

commitMutationEffects主要做了如下几件事

  1. 调用 commitDetachRef 解绑ref
  2. 根据 effectTag 执行对应的dom操作
  3. useLayoutEffect 销毁函数在 UpdateTag 时执行

现在让我们来看看操作dom的这几个函数

  • commitPlacement 插入节点:找到该节点 最近的parent节点和兄弟节点 ,然后根据 isContainer 来判断是 插入到兄弟节点前 还是 append到parent节点后
  • commitWork 更新节点:​ 如果 fibertagSimpleMemoComponent 则会调用 commitHookEffectListUnmount 执行对应的 hook 的销毁函数,可以看到传入的参数是 HookLayout | HookHasEffect ,也就是说 执行useLayoutEffect的销毁函数 。如果是 HostComponent ,那么调用 commitUpdatecommitUpdate 最后会调用updateDOMProperties 处理对应 Update 的dom操作
  • commitDeletion 删除节点: 如果是 ClassComponent 会执行 componentWillUnmount ,删除 fiber,如果是 FunctionComponent 会删除 ref、并执行 useEffect 的销毁函数,具体可在源码中查看 unmountHostComponentscommitNestedUnmountsdetachFiberMutation 这几个函数

commitLayoutEffectscommitMutationEffects 之后所有的dom操作都已经完成,可以访问dom了

  1. 调用 commitLayoutEffectOnFiber 执行相关 生命周期函数 或者 hook相关callback。​ 在源码中 commitLayoutEffectOnFiber 函数的别名是 commitLifeCycles,commitLifeCycles会判断fiber的类型,SimpleMemoComponent会执行useLayoutEffect的回调,然后调度useEffect,ClassComponent会执行componentDidMount或者componentDidUpdate,this.setState第二个参数也会执行,HostRoot会执行ReactDOM.render函数的第三个参数
  2. 执行 commitAttachRefref 赋值