useEffect
在commit阶段的before-mutation阶段之前,会使用scheduleCallback调度useEffect。可以看到useEffect的优先级是普通优先级。
flushPassiveEffectsImpl
主要做三件事,1 调用改useEffect在上一次render时的销毁函数; 2 调用该useEffect在本次render的回调函数。 3 存在同步任务的话,就执行他,不需要等到下次事件循环的宏任务。
1 调用useEffect的销毁函数。
commitPassiveUmmountEffects最终会调用这个commitHookEffectListUnmount函数,他会遍历
Effectlist,然后调用destory函数。useEffect的执行需要保证所有组件的useEffect的销毁函数执行完才能执行,因为多个组件可能公用一个ref,如果不是按照全部销毁再全部执行的顺序,那么组件的useEffect的销毁函数修改的ref.current可能影响另一个组件useEffect的执行。
2 调用useEffect的执行函数
遍历effectList,然后执行回调函数,获取destroy,存放在effect上,effect就是带有effectTag的fiber。
这就是useEffect的执行流程。
useEffect的调度顺序就是:
1 commit阶段的before-mutation阶段之前通过scheduleCallback进行调度flushPassiveEffects函数。
2 因为flushPassiveEffects函数会遍历effect,所以layout阶段之后,会将effectList放入一个全局变量。
3 适当的时机,useEffect会在页面渲染后,即layout阶段后执行。
4 flushPassiveEffects做的事情就是:获取effectList,遍历执行effect的useEffect销毁函数,然后再遍历执行effect的useEffect执行函数,将destory存放在每个fiber.destory上。
useRef
先看看useRef在mount和update的不同函数。
可以看到,创建了一个hooks结构,返回的是一个{current: xx}的值,而且存在了hook.memoizedState,对应之前说的useRef的hooks的数据结构存放memoizedState。
update的时候就简单粗暴了,直接返回hook.memoizedState.
ref的工作流程
在rect中,HostComponent和calssComponent可以赋值ref属性,而ForwardRef只是把ref传下去。
对于hostComponent,在commit阶段的mutation执行dom操作。对应的ref更新也是在mutation阶段。如果hostComponent或者classComponent有ref,那么
也会赋值对应的effectTag。
所以ref的工作流程分为两部分:
- 1 render阶段为含有ref属性的fiber添加Ref effectTag。
- 2 commit阶段为包含Ref effectTag的fiber执行对应操作。
render阶段
在beginwork阶段,
updateHostComponent和finishClassComponent会调用(对应hostComponent和classComponent)
,对其赋值ref属性。
在completeWork阶段,对于HostComponent,
如果当前的ref和之前保存的ref不同。表示原生组件的ref该换了。
commit阶段
mutation阶段,对于ref属性改变的情况,需要移除之前的ref。
而赋值是在layout阶段执行的
function commitAttachRef(finishedWork: Fiber) {
const ref = finishedWork.ref;
if (ref !== null) {
// 获取ref属性对应的Component实例
const instance = finishedWork.stateNode;
let instanceToUse;
switch (finishedWork.tag) {
case HostComponent:
instanceToUse = getPublicInstance(instance);
break;
default:
instanceToUse = instance;
}
// 赋值ref
if (typeof ref === 'function') {
ref(instanceToUse);
} else {
ref.current = instanceToUse;
}
}
}
至此ref的工作流程完毕。
总结:hostComponent和classComponent的ref是在render阶段被赋予effectTag属性,然后在commit阶段的时候,mutation阶段会判断ref是否变化,变化就去掉ref,然后在layout阶段重新赋值。
useMemo useCallback
mount的时候
之前介绍hooks的数据结构就说过,useMemo, useCallback创建的hooks上的memoizedState上存放着对应的值和依赖项。
创建hooks,useMemo会执行函数,然后将值和依赖项存储起来。
而useCallback会存粗函数和依赖项。
update的时候
再执行的时候,两者都会获取依赖项,然后进行匹配,如果改变就返回新的值,如果没改变,那么就返回存储在hooks的值。