跳到主要内容

react源码解析

Excerpt

全栈学习


useLayoutEffect销毁函数在UpdateTag时执行

function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
//遍历effectList
while (nextEffect !== null) {

const effectTag = nextEffect.effectTag;
// 调用commitDetachRef解绑ref
if (effectTag & Ref) {
const current = nextEffect.alternate;
if (current !== null) {
commitDetachRef(current);
}
}

// 根据effectTag执行对应的dom操作
const primaryEffectTag =
effectTag & (Placement | Update | Deletion | Hydrating);
switch (primaryEffectTag) {
// 插入dom
case Placement: {
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement;
break;
}
// 插入更新dom
case PlacementAndUpdate: {
// 插入
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement;
// 更新
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
//...
// 更新dom
case Update: {
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 删除dom
case Deletion: {
commitDeletion(root, nextEffect, renderPriorityLevel);
break;
}
}

nextEffect = nextEffect.nextEffect;
}
}

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

commitPlacement插入节点:

简化后的代码很清晰,找到该节点最近的parent节点和兄弟节点,然后根据isContainer来判断是插入到兄弟节点前还是append到parent节点后

unction commitPlacement(finishedWork: Fiber): void {
//...
const parentFiber = getHostParentFiber(finishedWork);//找到最近的parent

let parent;
let isContainer;
const parentStateNode = parentFiber.stateNode;
switch (parentFiber.tag) {
case HostComponent:
parent = parentStateNode;
isContainer = false;
break;
//...

}
const before = getHostSibling(finishedWork);//找兄弟节点
if (isContainer) {
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
} else {
insertOrAppendPlacementNode(finishedWork, before, parent);
}
}

commitWork更新节点:

在简化后的源码中可以看到

如果fiber的tag是SimpleMemoComponent会调用commitHookEffectListUnmount执行对应的hook的销毁函数,可以看到传入的参数是HookLayout | HookHasEffect,也就是说执行useLayoutEffect的销毁函数。

如果是HostComponent,那么调用commitUpdate,commitUpdate最后会调用updateDOMProperties处理对应Update的dom操作

function commitWork(current: Fiber | null, finishedWork: Fiber): void {
if (!supportsMutation) {
switch (finishedWork.tag) {
//...
case SimpleMemoComponent: {
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
}
//...
}
}

switch (finishedWork.tag) {
//...
case HostComponent: {
//...
commitUpdate(
instance,
updatePayload,
type,
oldProps,
newProps,
finishedWork,
);
}
return;
}
}
function updateDOMProperties(
domElement: Element,
updatePayload: Array<any>,
wasCustomComponentTag: boolean,
isCustomComponentTag: boolean,
): void {
// TODO: Handle wasCustomComponentTag
for (let i = 0; i < updatePayload.length; i += 2) {
const propKey = updatePayload[i];
const propValue = updatePayload[i + 1];
if (propKey === STYLE) {
setValueForStyles(domElement, propValue);
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
setInnerHTML(domElement, propValue);
} else if (propKey === CHILDREN) {
setTextContent(domElement, propValue);
} else {
setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
}
}
}


commitDeletion删除节点: 如果是ClassComponent会执行componentWillUnmount,删除fiber,如果是FunctionComponent 会删除ref、并执行useEffect的销毁函数,具体可在源码中查看unmountHostComponents、commitNestedUnmounts、detachFiberMutation这几个函数

function commitDeletion(
finishedRoot: FiberRoot,
current: Fiber,
renderPriorityLevel: ReactPriorityLevel,
): void {
if (supportsMutation) {
// Recursively delete all host nodes from the parent.
// Detach refs and call componentWillUnmount() on the whole subtree.
unmountHostComponents(finishedRoot, current, renderPriorityLevel);
} else {
// Detach refs and call componentWillUnmount() on the whole subtree.
commitNestedUnmounts(finishedRoot, current, renderPriorityLevel);
}
const alternate = current.alternate;
detachFiberMutation(current);
if (alternate !== null) {
detachFiberMutation(alternate);
}
}