commitDeletion 删除节点
commitDeletion具体做了以下几件事:
- 针对DOM节点,深度优先卸载ref,其次删除DOM节点
- 针对class组件,先卸载ref, 其次,深度优先遍历子节点,然后卸载ref以及删除DOM,并调用 componentWillUnmount 声明周期钩子函数;
- 针对function组件,跟class组件一致,会按照useEffect和useLayoutEffect的书写顺序,依次调用销毁函数;
typescript
function commitDeletion(current: Fiber): void {
if (supportsMutation) {
// ReactDOM走这个
unmountHostComponents(current);
} else {
// Detach refs and call componentWillUnmount() on the whole subtree.
commitNestedUnmounts(current);
}
// 卸载Ref
detachFiber(current);
}unmountHostComponents
这个方法主要做的就是具体删除操作;这个是一个深度优先的过程。
- 先找到当前节点的父节点(真实dom)
- 然后进行一个深度优先的遍历,优先子节点,在子节点的父节点,在父节点
typescript
function unmountHostComponents(current): void {
let node: Fiber = current;
// 标记当前的parent是否是一个真实的DOM节点
let currentParentIsValid = false;
let currentParent;
let currentParentIsContainer;
while (true) {
if (!currentParentIsValid) {
let parent = node.return;
// 这个循环的做的事情,其实就是向Fiber树向上查找
// 找到最近的为真实DOM父节点节点的Fiber
findParent: while (true) {
switch (parent.tag) {
case HostComponent:
currentParent = parent.stateNode;
currentParentIsContainer = false;
break findParent;
case HostRoot:
currentParent = parent.stateNode.containerInfo;
currentParentIsContainer = true;
break findParent;
case HostPortal:
currentParent = parent.stateNode.containerInfo;
currentParentIsContainer = true;
break findParent;
}
parent = parent.return;
}
currentParentIsValid = true;
}
if (node.tag === HostComponent || node.tag === HostText) {
// 提交嵌套卸载
commitNestedUnmounts(node);
// 执行真实DOM节点的removeChild操作
if (currentParentIsContainer) {
removeChildFromContainer(
((currentParent: any): Container),
(node.stateNode: Instance | TextInstance),
);
} else {
removeChild(
((currentParent: any): Instance),
(node.stateNode: Instance | TextInstance),
);
}
} else {
// 提交卸载
commitUnmount(node);
if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
}
// 如果到了当前节点, 结束
// 否则 遍历兄弟节点,结束后,再冒泡到父节点
if (node === current) {
return;
}
while (node.sibling === null) {
if (node.return === null || node.return === current) {
return;
}
node = node.return;
if (node.tag === HostPortal) {
currentParentIsValid = false;
}
}
node.sibling.return = node.return;
node = node.sibling;
}
}commitUnmount
- 针对函数组件, 调用useEffect和useLayoutEffect的卸载函数
- 针对class组件, 调用componentWillUnmount
- 针对DOM组件, 执行卸载ref相关操作
typescript
function commitUnmount(current: Fiber): void {
switch (current.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);
if (updateQueue !== null) {
const lastEffect = updateQueue.lastEffect;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
const destroy = effect.destroy;
if (destroy !== undefined) {
safelyCallDestroy(current, destroy);
}
effect = effect.next;
} while (effect !== firstEffect);
}
}
break;
}
case ClassComponent: {
safelyDetachRef(current);
const instance = current.stateNode;
if (typeof instance.componentWillUnmount === 'function') {
safelyCallComponentWillUnmount(current, instance);
}
return;
}
case HostComponent: {
safelyDetachRef(current);
return;
}
}
}commitNestedUnmounts
当前方法很commitUnmount方法其实做的事情差不多,只不过这里是针对纯DOM树节点,进行一个遍历卸载ref的过程
typescript
function commitNestedUnmounts(root: Fiber): void {
let node: Fiber = root;
while (true) {
commitUnmount(node);
if (
node.child !== null &&
(!supportsMutation || node.tag !== HostPortal)
) {
node.child.return = node;
node = node.child;
continue;
}
if (node === root) {
return;
}
while (node.sibling === null) {
if (node.return === null || node.return === root) {
return;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}detachFiber
将当前删除的节点的fiber对象置空,并且从Fiber链表上移除
typescript
function detachFiber(current: Fiber) {
current.return = null;
current.child = null;
current.memoizedState = null;
current.updateQueue = null;
const alternate = current.alternate;
if (alternate !== null) {
alternate.return = null;
alternate.child = null;
alternate.memoizedState = null;
alternate.updateQueue = null;
}
}