第三輪遍歷(oldFiber沒遍歷完且newChildren沒遍歷完):
- 把剩下的oldFiber放入existingChildren中;
- 聲明lastPlacedIndex變量脖咐,表示不需要移動(dòng)的老節(jié)點(diǎn)的索引;
- 如果在map中能找到相同key相同type的節(jié)點(diǎn)復(fù)用老fiber困乒,并從map中刪除;
- 如果在map中找不到相同key相同type的節(jié)點(diǎn)創(chuàng)建新fiber;
- 如果復(fù)用老fiber举娩,老fiber的索引大于lastPlacedIndex則更新lastPlacedIndex為老fiber的索引;
- 如果復(fù)用老fiber,老fiber的索引小于lastPlacedIndex則移動(dòng)fiber, lastPlacedIndex不變饰躲;
- 把map中剩下的fiber全部標(biāo)記為刪除
代碼詳情
- main.jsx
function FunctionComponent() {
const [number, setNumber] = React.useState(0);
return number === 0 ? (
<ul key="container" onClick={() => setNumber(number + 1)}>
<li key="A">A</li>
<li key="B" id="b">
B
</li>
<li key="C">C</li>
<li key="D">D</li>
<li key="E">E</li>
<li key="F" id="F">
F
</li>
</ul>
) : (
<ul key="container" onClick={() => setNumber(number + 1)}>
<li key="A">A2</li>
<li key="C">C2</li>
<li key="E">E2</li>
<li key="B" id="b2">
B2
</li>
<li key="G">G</li>
<li key="D">D2</li>
</ul>
);
}
let element = <FunctionComponent />;
const root = createRoot(document.getElementById("root"));
console.log("root", root);
root.render(element);
- ReactChildFiber
function placeChild(newFiber, lastPlacedIndex, newIdx) {
newFiber.index = newIdx;
if (!shouldTrackSideEffects) {
return lastPlacedIndex;
}
const current = newFiber.alternate;
if (current !== null) {
const oldIndex = current.index;
if (oldIndex < lastPlacedIndex) {
newFiber.flags |= Placement;
return lastPlacedIndex;
} else {
return oldIndex;
}
} else {
newFiber.flags |= Placement;
return lastPlacedIndex;
}
}
function mapRemainingChildren(returnFiber, currentFirstChild) {
const existingChildren = new Map();
let existingChild = currentFirstChild;
while (existingChild != null) {
if (existingChild.key != null) {
existingChildren.set(existingChild.key, existingChild);
} else {
existingChildren.set(existingChild.index, existingChild);
}
existingChild = existingChild.sibling;
}
return existingChildren;
}
function updateTextNode(returnFiber, current, textContent) {
if (current !== null || current.tag !== HostText) {
const created = createFiberFromText(textContent);
created.return = returnFiber;
return created;
} else {
const existing = useFiber(current, textContent);
existing.return = returnFiber;
return existing;
}
}
function updateFromMap(existingChildren, returnFiber, newIdx, newChild) {
if (
(typeof newChild === "string" && newChild !== "") ||
typeof newChild === "number"
) {
const matchedFiber = existingChildren.get(newIdx) || null;
return updateTextNode(returnFiber, matchedFiber, "" + newChild);
}
if (typeof newChild === "object" && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE: {
const matchedFiber =
existingChildren.get(
newChild.key === null ? newIdx : newChild.key
) || null;
return updateElement(returnFiber, matchedFiber, newChild);
}
}
}
}
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren) {
let resultingFirstChild = null;
let previousNewFiber = null;
let newIdx = 0;
let oldFiber = currentFirstChild;
let nextOldFiber = null;
let lastPlacedIndex = 0;
...
const existingChildren = mapRemainingChildren(returnFiber, oldFiber);
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = updateFromMap(
existingChildren,
returnFiber,
newIdx,
newChildren[newIdx]
);
if (newFiber !== null) {
if (shouldTrackSideEffects) {
if (newFiber.alternate !== null) {
existingChildren.delete(
newFiber.key === null ? newIdx : newFiber.key
);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
}
if (shouldTrackSideEffects) {
existingChildren.forEach((child) => deleteChild(returnFiber, child));
}
return resultingFirstChild;
}