在上一节 更新props 中,我们判断如果新旧dom的对应tag如果是一样的,name表示更新逻辑,如果不一样,则表示新建或删除。
可以从一个例子中去理解
let show = true
const Counter = () => {
function handleClick() {
show = !show
React.update()
}
const Foo = () => <div>foo</div>
const Bar = () => <p>bar</p>
return (
<div>
count:
<div>{show ? <Foo></Foo> : <Bar></Bar>}</div>
<button onClick={handleClick}>click</button>
</div>
)
}
上面例子中对应的链表图示如下。
从新旧链表中可以知道,需要将旧的<Foo />删掉,并新建一个<Bar />
let deletions = [] // 新建一个全局变量deletions,将需要删除的节点收集起来
function initChildren(fiber, children) {
let oldFiber = fiber.alternate?.child;
let prevChild = null;
children.forEach((child, index) => {
// 判断旧的tag与新tag是否相同
// true:更新节点
// false:创建、删除节点
const isSameTag = oldFiber && oldFiber.type === child.type;
let newFiber = null;
if (isSameTag) {
// 更新
// ...
} else {
// 新增
[// 注:edge case](<https://fortune-dungeon-43c.notion.site/edge-case-27343561d2194845a5ddd4a82c1686d8>)
if (child) {
newFiber = {
type: child.type,
props: child.props,
child: null,
sibling: null,
parent: fiber,
dom: null,
effectTag: "placement",
};
}
// 删除逻辑,将需要删除的节点收集起来
if (oldFiber) {
deletions.push(oldFiber);
}
}
// ...
});
// 如果旧的节点的字节点多余新节点的子节点,则需要把纠结点的子节点全部删除
// 所以循环调用,将sibling全部添加到deletions中
while (oldFiber) {
deletions.push(oldFiber);
oldFiber = oldFiber.sibling;
}
}
dom挂载环节是在链表创建完成后的 commitRoot()
函数中执行的,所以在挂载之前执行删除dom操作。
function commitRoot() {
deletions.forEach(commitDeletion); // 执行删除操作
commitWork(wipRoot.child);
currentRoot = wipRoot;
wipRoot = null;
deletions = []; // 删除完后清空
}
function commitDeletion(fiber) {
// step1.直接删除节点
// 问题:如果是函数组件的话,函数组件的dom是null
// 解决:递归查找子节点
// fiber.parent.dom.removeChild(fiber.dom)
// step2.递归查找子节点
// 问题:函数组件查找到子节点后,因为函数组件dom为null,执行删除时会发现fiber.parent.dom为null,同样有问题
// 解决:循环查找dom不为null的父节点
// if (fiber.dom) {
// fiber.parent.dom.removeChild(fiber.dom);
// } else {
// commitDeletion(fiber.child);
// }
// step3.循环查找dom不为null的父节点
if (fiber.dom) {
let fiberParent = fiber.parent;
while (!fiberParent.dom) {
fiberParent = fiberParent.parent;
}
fiberParent.dom.removeChild(fiber.dom);
} else {
commitDeletion(fiber.child);
}
}