【h站源码演示】【最佳启动源码】【新产品源码】acache源码

1.解读useEffect和useLayouEffect原理
2.解读useEffect和useLayouEffect原理

acache源码

解读useEffect和useLayouEffect原理

       背景

       写这篇文章是源码因为工作上不是非常繁忙,可以抽空学习自己常用框架和类库,源码深入理解它们,源码在技术上希望有更大的源码进步,培养学习兴趣;

useEffect

       和其它hooks一样,源码加载和更新执行不一样的源码h站源码演示方法(mountEffect和updateEffect);

1.mountEffect

       页面加载时,执行mountEffect;

       创建hook对象,源码加入组件的源码hook单向链表;

       在组件的fiber的flag中加入副作用相关的effectTag;(加载期间默认有layoutEffect和effect的副作用)

       创建effect对象,给hook对象的源码memoizedState和加入组件fiber的updateQueue中形成effect环状链表;在渲染工作完成后,会循环这个环状链表,源码执行每个effect对象的源码destory和create;

consteffect={ tag,create,destroy,deps,next:null};tag是effect的类型tag为9是useEffect,5是useLayoutEffectcreate是useEffect或useLayoutEffect的回调函数destroy是create返回的回调函数deps是useEffect或useLayoutEffect的依赖数组next指向下个effect对象;1.1.effect环状链表图functionpushEffect(tag,create,destroy,deps){ consteffect={ tag,create,destroy,deps,next:null};//新创建的effect对象为最后为effect链表的一个effect对象,componentUpdateQueue.lastEffect会指向新创建的源码effect对象//新创建的effect对象的next会指向第一个effct对象;letcomponentUpdateQueue=(currentlyRenderingFiber.updateQueue);if(componentUpdateQueue===null){ //当前没有updateQueuecomponentUpdateQueue=createFunctionComponentUpdateQueue();//创建updateQueuecurrentlyRenderingFiber.updateQueue=componentUpdateQueue;//形成一个环状链表componentUpdateQueue.lastEffect=effect.next=effect}else{ constlastEffect=componentUpdateQueue.lastEffect;if(lastEffect===null){ componentUpdateQueue.lastEffect=effect.next=effect;}else{ //第一个effect对象为最先创建的的effect对象constfirstEffect=lastEffect.next;//获取第一个effect对象lastEffect.next=effect;//旧的最后一个effect对象的next,指向新创建的源码effecteffect.next=firstEffect;//新创建的effect对象的next指向第一个effectcomponentUpdateQueue.lastEffect=effect;//updateQueue的lastEffect指向effect,新创建的源码effect变为最后一个effect对象}}returneffect;}2.updateEffect

       页面更新时,执行updateEffect;

       根据hook单向链表获取对应的源码最佳启动源码更新时的hook对象,创建新的hook对象,加入hook单向链表;

       如果effect的deps不为null,或者undefined,会从当前hook对象拿到上一次effect对象,再从effect对象拿到deps和destroy,用新的deps与之比较;

       如果新老deps相等,push一个不带HookHasEffect的新产品源码tag给effect对象,加入updateQueue环状链表(这个effect不会被标记为有副作用,所以,effect的create和destroy不会被执行),不更新hook.memoizedState;

       如果新老deps不相等,更新effect对象,在effect的tag中加入HookHasEffect和上一次create执行的destroy,更新hook.memoizedState;

3.useEffct的传奇hero源码回调函数和销毁函数的执行时机

       在render时期构建effect链表;在commit时执行先执行之前没有执行完的useEffect,然后,在beforeMutation阶段操作dom前,以NormalPriority常规优先级添加一个异步任务到任务队列(这个异步任务是用来执行useEffect的destroy和create的),在layout阶段完成,页面完成渲染后,执行在beforeMutation阶段添加的异步任务;

3.1.commit开始时

       主要是为了执行之前没有执行的useEffect

       进入commit阶段,这和useEffect异步调度的lualfs源码下载特点有关,它以一般的优先级被调度,意味着一旦有更高优先级的任务进入到commit阶段,上一次任务的useEffect还没得到执行。所以在本次更新开始前,需要先将之前的useEffect都执行掉,以保证本次调度的useEffect都是本次更新产生的。

functioncommitRootImpl(root,recoverableErrors,renderPriorityLevel){ do{ //`flushPassiveEffects`willcall`flushSyncUpdateQueue`attheend,which//means`flushPassiveEffects`willsometimesresultinadditional//passiveeffects.Soweneedtokeepflushinginaloopuntilthereare//nomorependingeffects.//TODO:Mightbebetterif`flushPassiveEffects`didnotautomatically//flushsynchronousworkattheend,toavoidfactoringhazardslikethis.flushPassiveEffects();}while(rootWithPendingPassiveEffects!==null);...省略代码}3.2.beforeMutation

       只会发起一次useEffect调度,是异步调度,以NormalPriority常规优先级添加一个异步任务在任务队列中(push(timerQueue,newTask)),在页面渲染完成时,会执行这个异步任务

functioncommitRootImpl(root,recoverableErrors,renderPriorityLevel){ ...省略代码if((finishedWork.subtreeFlags&PassiveMask)!==NoFlags||(finishedWork.flags&PassiveMask)!==NoFlags){ if(!rootDoesHavePassiveEffects){ rootDoesHavePassiveEffects=true;scheduleCallback$1(NormalPriority,function(){ //添加一个异步任务到任务队列flushPassiveEffects();//Thisrendertriggeredpassiveeffects:releasetherootcachepool//*after*passiveeffectsfiretoavoidfreeingacachepoolthatmay//bereferencedbyanodeinthetree(HostRoot,Cacheboundaryetc)returnnull;});}}...省略代码}3.3.layout

       加载时,只执行useEffect的create函数即可;

       如果pendingPassiveEffectsLanes是同步赛道,就在页面渲染完直接执行useEffect的create和destroy,在beforeMutation时添加的异步任务,不会执行useEffect的create和destory

if(includesSomeLane(pendingPassiveEffectsLanes,SyncLane)&&root.tag!==LegacyRoot){ //加载期间默认是不走这里的//这里也是执行useEffect的create,如果pendingPassiveEffectsLanes是同步赛道,//就在渲染完成后直接执行useEffect的create和destory//在beforeMutation时添加的异步任务执行时,不会执行useEffect的create和destoryflushPassiveEffects();}

       执行上一次useEffect的create返回的destroy,拿到函数组件fiber的updateQueue,循环这个effect环状链表,拿到effect对象的destroy执行;

functioncommitHookEffectListUnmount(flags,finishedWork,nearestMountedAncestor){ varupdateQueue=finishedWork.updateQueue;varlastEffect=updateQueue!==null?updateQueue.lastEffect:null;if(lastEffect!==null){ varfirstEffect=lastEffect.next;vareffect=firstEffect;do{ if((effect.tag&flags)===flags){ //Unmountvardestroy=effect.destroy;effect.destroy=undefined;if(destroy!==undefined){ { if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectUnmountStarted(finishedWork);}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectUnmountStarted(finishedWork);}}safelyCallDestroy(finishedWork,nearestMountedAncestor,destroy);//执行destroy{ if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectUnmountStopped();}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectUnmountStopped();}}}}effect=effect.next;}while(effect!==firstEffect);}}

       执行完所有组件的destroy,再执行create;同理,也是拿到函数组件fiber的updateQueue,循环这个effect环状链表,拿到effect对象的create执行,然后把create返回的destroy给effect对象(留着下着更新执行useEffect时用);

functioncommitHookEffectListMount(flags,finishedWork){ varupdateQueue=finishedWork.updateQueue;varlastEffect=updateQueue!==null?updateQueue.lastEffect:null;if(lastEffect!==null){ varfirstEffect=lastEffect.next;vareffect=firstEffect;do{ if((effect.tag&flags)===flags){ { if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectMountStarted(finishedWork);}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectMountStarted(finishedWork);}}//Mountvarcreate=effect.create;effect.destroy=create();{ if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectMountStopped();}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectMountStopped();}}{ vardestroy=effect.destroy;if(destroy!==undefined&&typeofdestroy!=='function'){ varhookName=void0;if((effect.tag&Layout)!==NoFlags){ hookName='useLayoutEffect';}elseif((effect.tag&Insertion)!==NoFlags){ hookName='useInsertionEffect';}else{ hookName='useEffect';}varaddendum=void0;if(destroy===null){ addendum='Youreturnednull.Ifyoureffectdoesnotrequireclean'+'up,returnundefined(ornothing).';}elseif(typeofdestroy.then==='function'){ addendum='\n\nItlookslikeyouwrote'+hookName+'(async()=>...)orreturnedaPromise.'+'Instead,writetheasyncfunctioninsideyoureffect'+'andcallitimmediately:\n\n'+hookName+'(()=>{ \n'+'asyncfunctionfetchData(){ \n'+'//Youcanawaithere\n'+'constresponse=awaitMyAPI.getData(someId);\n'+'//...\n'+'}\n'+'fetchData();\n'+"},[someId]);//Or[]ifeffectdoesn'tneedpropsorstate\n\n"+'LearnmoreaboutdatafetchingwithHooks:/post/

解读useEffect和useLayouEffect原理

       èƒŒæ™¯

       å†™è¿™ç¯‡æ–‡ç« æ˜¯å› ä¸ºå·¥ä½œä¸Šä¸æ˜¯éžå¸¸ç¹å¿™ï¼Œå¯ä»¥æŠ½ç©ºå­¦ä¹ è‡ªå·±å¸¸ç”¨æ¡†æž¶å’Œç±»åº“,深入理解它们,在技术上希望有更大的进步,培养学习兴趣;

useEffect

       å’Œå…¶å®ƒhooks一样,加载和更新执行不一样的方法(mountEffect和updateEffect);

1.mountEffect

       é¡µé¢åŠ è½½æ—¶ï¼Œæ‰§è¡ŒmountEffect;

       åˆ›å»ºhook对象,加入组件的hook单向链表;

       åœ¨ç»„件的fiber的flag中加入副作用相关的effectTag;(加载期间默认有layoutEffect和effect的副作用)

       åˆ›å»ºeffect对象,给hook对象的memoizedState和加入组件fiber的updateQueue中形成effect环状链表;在渲染工作完成后,会循环这个环状链表,执行每个effect对象的destory和create;

consteffect={ tag,create,destroy,deps,next:null};tag是effect的类型tag为9是useEffect,5是useLayoutEffectcreate是useEffect或useLayoutEffect的回调函数destroy是create返回的回调函数deps是useEffect或useLayoutEffect的依赖数组next指向下个effect对象;1.1.effect环状链表图functionpushEffect(tag,create,destroy,deps){ consteffect={ tag,create,destroy,deps,next:null};//新创建的effect对象为最后为effect链表的一个effect对象,componentUpdateQueue.lastEffect会指向新创建的effect对象//新创建的effect对象的next会指向第一个effct对象;letcomponentUpdateQueue=(currentlyRenderingFiber.updateQueue);if(componentUpdateQueue===null){ //当前没有updateQueuecomponentUpdateQueue=createFunctionComponentUpdateQueue();//创建updateQueuecurrentlyRenderingFiber.updateQueue=componentUpdateQueue;//形成一个环状链表componentUpdateQueue.lastEffect=effect.next=effect}else{ constlastEffect=componentUpdateQueue.lastEffect;if(lastEffect===null){ componentUpdateQueue.lastEffect=effect.next=effect;}else{ //第一个effect对象为最先创建的的effect对象constfirstEffect=lastEffect.next;//获取第一个effect对象lastEffect.next=effect;//旧的最后一个effect对象的next,指向新创建的effecteffect.next=firstEffect;//新创建的effect对象的next指向第一个effectcomponentUpdateQueue.lastEffect=effect;//updateQueue的lastEffect指向effect,新创建的effect变为最后一个effect对象}}returneffect;}2.updateEffect

       é¡µé¢æ›´æ–°æ—¶ï¼Œæ‰§è¡ŒupdateEffect;

       æ ¹æ®hook单向链表获取对应的更新时的hook对象,创建新的hook对象,加入hook单向链表;

       å¦‚æžœeffect的deps不为null,或者undefined,会从当前hook对象拿到上一次effect对象,再从effect对象拿到deps和destroy,用新的deps与之比较;

       å¦‚果新老deps相等,push一个不带HookHasEffect的tag给effect对象,加入updateQueue环状链表(这个effect不会被标记为有副作用,所以,effect的create和destroy不会被执行),不更新hook.memoizedState;

       å¦‚果新老deps不相等,更新effect对象,在effect的tag中加入HookHasEffect和上一次create执行的destroy,更新hook.memoizedState;

3.useEffct的回调函数和销毁函数的执行时机

       åœ¨render时期构建effect链表;在commit时执行先执行之前没有执行完的useEffect,然后,在beforeMutation阶段操作dom前,以NormalPriority常规优先级添加一个异步任务到任务队列(这个异步任务是用来执行useEffect的destroy和create的),在layout阶段完成,页面完成渲染后,执行在beforeMutation阶段添加的异步任务;

3.1.commit开始时

       ä¸»è¦æ˜¯ä¸ºäº†æ‰§è¡Œä¹‹å‰æ²¡æœ‰æ‰§è¡Œçš„useEffect

       è¿›å…¥commit阶段,这和useEffect异步调度的特点有关,它以一般的优先级被调度,意味着一旦有更高优先级的任务进入到commit阶段,上一次任务的useEffect还没得到执行。所以在本次更新开始前,需要先将之前的useEffect都执行掉,以保证本次调度的useEffect都是本次更新产生的。

functioncommitRootImpl(root,recoverableErrors,renderPriorityLevel){ do{ //`flushPassiveEffects`willcall`flushSyncUpdateQueue`attheend,which//means`flushPassiveEffects`willsometimesresultinadditional//passiveeffects.Soweneedtokeepflushinginaloopuntilthereare//nomorependingeffects.//TODO:Mightbebetterif`flushPassiveEffects`didnotautomatically//flushsynchronousworkattheend,toavoidfactoringhazardslikethis.flushPassiveEffects();}while(rootWithPendingPassiveEffects!==null);...省略代码}3.2.beforeMutation

       åªä¼šå‘起一次useEffect调度,是异步调度,以NormalPriority常规优先级添加一个异步任务在任务队列中(push(timerQueue,newTask)),在页面渲染完成时,会执行这个异步任务

functioncommitRootImpl(root,recoverableErrors,renderPriorityLevel){ ...省略代码if((finishedWork.subtreeFlags&PassiveMask)!==NoFlags||(finishedWork.flags&PassiveMask)!==NoFlags){ if(!rootDoesHavePassiveEffects){ rootDoesHavePassiveEffects=true;scheduleCallback$1(NormalPriority,function(){ //添加一个异步任务到任务队列flushPassiveEffects();//Thisrendertriggeredpassiveeffects:releasetherootcachepool//*after*passiveeffectsfiretoavoidfreeingacachepoolthatmay//bereferencedbyanodeinthetree(HostRoot,Cacheboundaryetc)returnnull;});}}...省略代码}3.3.layout

       åŠ è½½æ—¶ï¼Œåªæ‰§è¡ŒuseEffect的create函数即可;

       å¦‚æžœpendingPassiveEffectsLanes是同步赛道,就在页面渲染完直接执行useEffect的create和destroy,在beforeMutation时添加的异步任务,不会执行useEffect的create和destory

if(includesSomeLane(pendingPassiveEffectsLanes,SyncLane)&&root.tag!==LegacyRoot){ //加载期间默认是不走这里的//这里也是执行useEffect的create,如果pendingPassiveEffectsLanes是同步赛道,//就在渲染完成后直接执行useEffect的create和destory//在beforeMutation时添加的异步任务执行时,不会执行useEffect的create和destoryflushPassiveEffects();}

       æ‰§è¡Œä¸Šä¸€æ¬¡useEffect的create返回的destroy,拿到函数组件fiber的updateQueue,循环这个effect环状链表,拿到effect对象的destroy执行;

functioncommitHookEffectListUnmount(flags,finishedWork,nearestMountedAncestor){ varupdateQueue=finishedWork.updateQueue;varlastEffect=updateQueue!==null?updateQueue.lastEffect:null;if(lastEffect!==null){ varfirstEffect=lastEffect.next;vareffect=firstEffect;do{ if((effect.tag&flags)===flags){ //Unmountvardestroy=effect.destroy;effect.destroy=undefined;if(destroy!==undefined){ { if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectUnmountStarted(finishedWork);}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectUnmountStarted(finishedWork);}}safelyCallDestroy(finishedWork,nearestMountedAncestor,destroy);//执行destroy{ if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectUnmountStopped();}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectUnmountStopped();}}}}effect=effect.next;}while(effect!==firstEffect);}}

       æ‰§è¡Œå®Œæ‰€æœ‰ç»„件的destroy,再执行create;同理,也是拿到函数组件fiber的updateQueue,循环这个effect环状链表,拿到effect对象的create执行,然后把create返回的destroy给effect对象(留着下着更新执行useEffect时用);

functioncommitHookEffectListMount(flags,finishedWork){ varupdateQueue=finishedWork.updateQueue;varlastEffect=updateQueue!==null?updateQueue.lastEffect:null;if(lastEffect!==null){ varfirstEffect=lastEffect.next;vareffect=firstEffect;do{ if((effect.tag&flags)===flags){ { if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectMountStarted(finishedWork);}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectMountStarted(finishedWork);}}//Mountvarcreate=effect.create;effect.destroy=create();{ if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectMountStopped();}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectMountStopped();}}{ vardestroy=effect.destroy;if(destroy!==undefined&&typeofdestroy!=='function'){ varhookName=void0;if((effect.tag&Layout)!==NoFlags){ hookName='useLayoutEffect';}elseif((effect.tag&Insertion)!==NoFlags){ hookName='useInsertionEffect';}else{ hookName='useEffect';}varaddendum=void0;if(destroy===null){ addendum='Youreturnednull.Ifyoureffectdoesnotrequireclean'+'up,returnundefined(ornothing).';}elseif(typeofdestroy.then==='function'){ addendum='\n\nItlookslikeyouwrote'+hookName+'(async()=>...)orreturnedaPromise.'+'Instead,writetheasyncfunctioninsideyoureffect'+'andcallitimmediately:\n\n'+hookName+'(()=>{ \n'+'asyncfunctionfetchData(){ \n'+'//Youcanawaithere\n'+'constresponse=awaitMyAPI.getData(someId);\n'+'//...\n'+'}\n'+'fetchData();\n'+"},[someId]);//Or[]ifeffectdoesn'tneedpropsorstate\n\n"+'LearnmoreaboutdatafetchingwithHooks:/post/

更多内容请点击【知识】专栏

精彩资讯