import { ContentReset, Deletion, Placement, PlacementAndUpdate, Ref, Update } from "../reconciler/FiberFlags"
let firstEffect = {
name: 'a',
nextEffect: {
name: 'b',
nextEffect: {
name: 'c',
nextEffect: null
}
}
}
const root = {
firstEffect
}
let nextEffect = null
function commitRoot(root) {
let firstEffect = root.firstEffect
if (firstEffect !== null) {
console.log('has effect')
nextEffect = firstEffect
commitBeforeMutationEffects()
nextEffect = firstEffect
commitMutationEffects(root)
nextEffect = firstEffect
commitLayoutEffects(root)
}
}
function commitBeforeMutationEffects() {
console.log('function commit before mutation effects')
while(nextEffect !== null) {
nextEffect = nextEffect.nextEffect
}
}
function commitMutationEffects(root) {
console.log('function commit Mutation Effects')
while(nextEffect !== null) {
console.log(nextEffect.name)
const flags = nextEffect.flags
if (flags & ContentReset) {
// éç½® text å
容
}
if (flags & Ref) {
// æ¸
空 ref
}
const primaryFlags = falgs & (Placement | Update | Deletion)
switch(primaryFlags) {
case PlacementAndUpdate:
commitPlacement(nextEffect)
nextEffect.flags &= ~Placement
const current = nextEffect.alternate
commitWork(current, nextEffect)
break
}
nextEffect = nextEffect.nextEffect
}
}
function commitLayoutEffects(root) {
console.log('function commit layout effects')
}
commitRoot(root)
// commitRootImplæ¹æ³
// before mutationä¹å
do {
// 触åuseEffectåè°ä¸å
¶ä»åæ¥ä»»å¡ãç±äºè¿äºä»»å¡å¯è½è§¦åæ°ç渲æï¼æä»¥è¿éè¦ä¸ç´éåæ§è¡ç´å°æ²¡æä»»å¡
flushPassiveEffects();
} while (rootWithPendingPassiveEffects !== null);
// rootæ fiberRootNode
// root.finishedWorkæå½ååºç¨çrootFiber? TODO
const finishedWork = root.finishedWork;
// 塿¯åéå带laneç齿¯ä¼å
级ç¸å
³
const lanes = root.finishedLanes;
if (finishedWork === null) {
return null;
}
root.finishedWork = null;
root.finishedLanes = NoLanes;
// éç½®Schedulerç»å®çåè°å½æ°
root.callbackNode = null;
root.callbackId = NoLanes;
let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
// éç½®ä¼å
级ç¸å
³åé
markRootFinished(root, remainingLanes);
// æ¸
é¤å·²å®æçdiscrete updatesï¼ä¾å¦ï¼ç¨æ·é¼ æ ç¹å»è§¦åçæ´æ°ã
if (rootsWithPendingDiscreteUpdates !== null) {
if (
!hasDiscreteLanes(remainingLanes) &&
rootsWithPendingDiscreteUpdates.has(root)
) {
rootsWithPendingDiscreteUpdates.delete(root);
}
}
// éç½®å
¨å±åé
if (root === workInProgressRoot) {
workInProgressRoot = null;
workInProgress = null;
workInProgressRootRenderLanes = NoLanes;
} else {
}
// å°effectListèµå¼ç»firstEffect
// ç±äºæ¯ä¸ªfiberçeffectListåªå
å«ä»çååèç¹ TODO
// æä»¥æ ¹èç¹å¦ææeffectTagåä¸ä¼è¢«å
å«è¿æ¥
// æä»¥è¿éå°æeffectTagçæ ¹èç¹æå
¥å°effectListå°¾é¨
// è¿æ ·æè½ä¿è¯æeffectçfiberé½å¨effectListä¸
let firstEffect;
// æ ¹èç¹æeffectçæ
åµ
if (finishedWork.effectTag > PerformedWork) {
if (finishedWork.lastEffect !== null) {
//妿已ç»åå¨lastEffectï¼åææ ¹èç¹æ¾å¨effecté¾çæå
finishedWork.lastEffect.nextEffect = finishedWork;
firstEffect = finishedWork.firstEffect;
} else {
// å¦ææ²¡ælastEffect,åææ ¹èç¹æ¾å¨effecté¾çæå
firstEffect = finishedWork;
}
} else {
// æ ¹èç¹æ²¡æeffectTag
firstEffect = finishedWork.firstEffect;
}
// layouté¶æ®µä¹å
/**
* 主è¦
*/
const rootDidHavePassiveEffects = rootDoseHavePassiveEffects;
// useEffectç¸å
³
if(rootDoseHavePassiveEffects){
rootDoseHavePassiveEffects = false;
rootWithPendingPassiveEffects = root;
pendingPassiveEffectsLanes = lanes;
pendingPassiveEffectsRenderPriority = renderPriorityLevel;
} else {}
// æ§è½ä¼åç¸å
³
if(remainingLanes !== NoLanes) {
if(enableSchedulerTracing){
}
} else {
}
if(enableSchedulerTracing){
if(!rootDidHavePassiveEffects){
}
}
// æ£æ¥æ é循ç¯ç忥任å¡
if(ramainingLanes === SyncLane){
}
// å¨ç¦»å¼commitRoot彿°åè°ç¨ï¼ 触å䏿¬¡æ°çè°åº¦ï¼ç¡®ä¿ä»»ä½éå çä»»å¡è¢«è°åº¦
ensureRootIsScheduled(root, now());
// ...å¤çæªæè·é误åèçæ¬éççè¾¹çé®é¢
// æ§è¡åæ¥ä»»å¡ï¼è¿æ ·åæ¥ä»»å¡ä¸éè¦çå°ä¸æ¬¡äºä»¶å¾ªç¯å¨æ§è¡
// æ¯å¦componentDidxxx䏿§è¡setStateï¼useLayoutEffectä¸åå»ºçæ´æ°ä¼å¨è¿éè¢«åæ¥æ§è¡
flushSyncCallbackQueue();
return null;
// before mutation é¶æ®µ
// éç¹å¨ä¸é¢è¿ä¸ªå½æ°
function commitBeforeMutationEffects(finishedWork){
while (nextEffect !== null) {
const current = nextEffect.alternate;
if(!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null){
// focus blurç¸å
³
}
const effectTag = nextEffect.effectTag
// è°ç¨getSnapshotBeforeUpdate
if((effectTag & Snapshot) !== NoEffect) {
commitBeforeMutationEffectsOnFiber(current, nextEffect);
}
// è°ç¨useEffect
if((effectTag & Passive) !== NoEffect) {
if(!rootDoseHavePassiveEffects) {
rootDoseHavePassiveEffects = true;
// ç±Schedule模åæä¾ï¼ç¨äºä»¥æä¸ªä¼å
çº§å¼æ¥è°åº¦ä¸ä¸ªåè°å½æ°
scheduleCallback(NormalSchedulerPriority, () => {
// 触åuseEffect
flushPassiveEffects();
return null;
});
}
}
nextEffect = nextEffect.nextEffect
}
}
// mutation é¶æ®µ
nextEffect = firstEffect;
do{
try{
commitMutationEffects(root, renderPriorityLevel);
} catch(error) {
invariant(nextEffect !== null, 'Should be working on an effect.');
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
} while (nextEffect !== null)
// mutationé¶æ®µæ ¸å¿
function commitMutationEffects(){
root: FiberRoot,
renderPriorityLevel
} {
// éåeffectList
while(nextEffect !== null) {
const effectTag = nextEffect.effectTag;
// æ ¹æ®ContentReset effectTagéç½®æåèç¹
if(effectTag & ContentReset){
commitResetTextContent(nextEffect);
}
// æ´æ°ref
if(effectTag & Ref){
const current = nextEffect.alternate;
if(current !== null){
commitDetachRef(current);
}
}
// æ ¹æ®effectTagåå«å¤ç
const primaryEffectTag = effectTag & (Placement | Update | Deletion | Hydrating)
switch (primaryEffectTag) {
// æå
¥DOM
case Placement: {
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement; // è®°å½æ§è¡åçç¶æ
break;
}
// æå
¥DOMå¹¶æ´æ°DOM
case PlacementAndUpdate: {
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement;
// æ´æ°DOM
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// SSRç¸å
³æä½ ...ç¥
case Update: {
// æ´æ°DOM
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
case Deletion: {
// å é¤DOM
commitDeletion(root, nextEffect, renderPriortyLevel);
break;
}
}
nextEffect = nextEffect.nextEffect;
}
}
function commitPlacement(nextEffect){
// è·åç¶çº§DOMèç¹ãå
¶ä¸finishedWorkä¸ºä¼ å
¥çFiberèç¹
const parentFiber = getHostParentFiber(finishedWork);
// ç¶çº§DOMèç¹
const parentStateNode = parentFiber.stateNode;
// è·åFiberèç¹çDOMå
å¼èç¹
const before = getHostSibling(finishedWork);
// æ ¹æ®å
å¼èç¹æ¯å¦åå¨å³å®è°ç¨ parentNode.insertBefore æ parentNode.appendChildæ§è¡DOMæå
¥æä½
if(isContainer){
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
} else {
insertOrAppendPlacementNode(finishedWork, before, parent)
}
}
function commitUpdate() {
// æ ¹æ®Fiber.Tagåå«å¤ç
// tag为FunctionComponentçæ
åµ
// è¯¥æ¹æ³ä¼éåeffectList æ§è¡ææuseLayoutEffect hookç鿝彿°
commitHookEffectListUnmonut()
// tag为HostComponentçæ
åµ
for(let i = 0; i < updatePayload.length; i += 2){
const propKey = updatePayload[i];
const propValue = updatePayload[i + 1];
// å¤ç style
if(propKey === STYLE){
setValueForStyles(domElement, propValue);
} else if (propKey === DANGEROUSLY_SET_INNER_HTML){
setInnerHtml(domElement, propValue);
} else if (propKey === CHILDREN) {
setTextContent(domElement, propValue)
} else {
// å¤çå©ä½props
setValueForProperty(domElement, propKey, propValuem, isCustomComponentTag);
}
}
}
function commitDeletion() {
/**
* éå½è°ç¨ClassComponentçcomponentWillUnmountï¼ä»é¡µé¢ç§»é¤DOM
* è§£ç»ref
* è°åº¦useEffectç鿝彿°
*/
}
// layout é¶æ®µ
/**
* è¿ä¸ªé¶æ®µæ¯å¨DOMå·²ç»æ¸²æå®æåæ§è¡çï¼å¨è¿ä¸ªé¶æ®µè§¦åççå½å¨æåhookå¯ä»¥ç´æ¥è®¿é®å·²æ¹åçDOM
*/
root.current = finishedWork;
nextEffect = firstEffect;
do{
try{
commitLayoutEffects(root, lanes);
} catch(error) {
invariant(nextEffect !== null, "Should be working on an effect.");
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
} while (nextEffect !== null);
nextEffect = null;
function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes){
while(nextEffect !== null){
const effectTag = nextEffect.effectTag;
// è°ç¨çå½å¨æé©ååhook
if(effectTag & (Update | Callback)){
const current = nextEffect.alternate;
commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
}
// èµå¼ref
if(effectTag & Ref) {
commitAttachRef(nextEffect);
}
nextEffect = nextEffect.nextEffect
}
}
function commitLayoutEffectOnFiber() {
// æ ¹æ®fiber.tag对ä¸åç±»åèç¹åå«å¤ç
// 对äºClassComponentï¼ ä¼éè¿current === null åºåæ¯mountè¿æ¯updateï¼è°ç¨componentDidMount æcomponentDidUpdate
// 触åç¶ææ´æ°çthis.setState妿èµå¼äºç¬¬äºä¸ªåæ°åè°å½æ°ï¼ä¹ä¼å¨æ¤æ¶è°ç¨ã
// 对äºFunctionCompnentåç¸å
³ç±»å
switch (finishedWork.tag) {
// 以ä¸é½æ¯FunctionComponentåç¸å
³ç±»å
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block: {
// æ§è¡useLayoutEffectçåè°å½æ°
commmitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
// è°åº¦useEffectç鿝彿°ä¸åè°å½æ°
schedulePassiveEffects(finishedWork);
return;
}
}
}
// æ·±å
¥flushPassiveEffects ï¼ä¸æææä¸ºï¼æ¸
æ´è¢«å¨ææï¼
// flushPassiveEffectså
é¨ä¼è®¾ç½®ä¼å
级ï¼å¹¶æ§è¡flushPassiveEffectsImplï¼Implçæææ¯å®ç°ï¼å³implementationçç®å
/**
* flushPassiveEffectsImpl主è¦åä¸ä»¶äº
* 1. è°ç¨è¯¥useEffectå¨ä¸ä¸æ¬¡renderæ¶ç鿝彿°
* 2. è°ç¨è¯¥useEffect卿¬æ¬¡renderæ¶çåè°å½æ°
* 3. 妿åå¨åæ¥ä»»å¡ï¼ä¸éè¦çå¾
䏿¬¡äºä»¶å¾ªç¯çå®ä»»å¡ï¼æåæ§è¡
* 1,2两æ¥ä¼å¨layouté¶æ®µä¹å弿¥æ§è¡
*/
// é¶æ®µä¸ï¼éæ¯å½æ°çæ§è¡
/**
* useEffectä¼ä¿è¯å¨æ§è¡å®å
¨é¨ç鿝彿°ä¹åï¼åæ§è¡åè°å½æ°
*/
// pendingPassiveHookEffectsUnmountä¸ä¿åäºææéè¦æ§è¡éæ¯çuseEffect
const unmountEffects = pendingPassiveHookEffectsUnmount;
pendingPassiveHookEffectsUnmount = [];
for (let i = 0; i < unmountEffects.length; i += 2) {
const effect = ((unmountEffects[i]: any): HookEffect);
const fiber = ((unmountEffects[i + 1]: any): Fiber);
const distroy = effect.distroy;
effect.distroy = undefined;
if(typeof destroy == 'function') {
// 鿝彿°åå¨åæ§è¡
try {
destroy();
} catch (error) {
captureCommitPhaseError(fiber, error);
}
}
}
function schedulePassiveEffects(finishedWork: Fiber) {
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if(lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
const { next, tag } = effect;
if(
(tag & HookPassive) !== NoHookEffect &&
(tag & HookHasEffect) !== NoHookEffect
) {
// åpendingPassiveHookEffectsUnmountæ°ç»å
pushè¦éæ¯çeffect
enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
// åpendingPassiveHookEffectMountæ°ç»å
pushè¦æ§è¡åè°çeffect
enqueuePendingPassiveHookEffectMount(finishedWork, effect);
}
effect = next;
} while ( effect !== firstEffect );
}
}
// é¶æ®µäºï¼ åè°å½æ°çæ§è¡
const mountEffects = pendingPassiveHookEffectsMount;
pendingPassiveHookEffectsMount = [];
for(let i = 0; i < mountEffects.length; i += 2) {
const effect = ((mountEffects[i]: any): HookEffect);
const fiber = ((mountEffects[i + 1]: any): Fiber);
try {
const create = effect.create;
effect.distroy = create();
} catch (error) {
captureCommitPhaseError(fiber, error);
}
}