react進階第八講:key
阿新 • • 發佈:2021-12-10
diff
step1: 遍歷新children,複用 oldFiber
React 在一次更新中, 當children是一個數組的話,會呼叫reconcileChildrenArray來調和子代 fiber。
function reconcileChildrenArray(){ /* 第一步 */ for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) { if (oldFiber.index > newIdx) { // 遍歷完newChild後,oldChild還有剩餘 nextOldFiber = oldFiber; oldFiber = null; } else { nextOldFiber = oldFiber.sibling; } const newFiber = updateSlot(returnFiber,oldFiber,newChildren[newIdx],expirationTime,); // 匹配複用老 fiber if (newFiber === null) { break } // ..一些其他邏輯 } if (shouldTrackSideEffects) { // shouldTrackSideEffects 為更新流程。 if (oldFiber && newFiber.alternate === null) { /* 找到了與新節點對應的fiber,但是不能複用,那麼直接刪除老節點 */ deleteChild(returnFiber, oldFiber); } } } }
- 使用sibling指向同級兄弟節點。所以在遍歷children 遍歷,sibling 指標同時移動,找到與 child 對應的 oldFiber 。
- updateSlot 內部會判斷當前的 tag 和 key 是否匹配,如果匹配複用老 fiber 形成新的 fiber ,如果不匹配,返回 null 。
step2: 統一刪除oldfiber
if (newIdx === newChildren.length) {
deleteRemainingChildren(returnFiber, oldFiber);
return resultingFirstChild;
}
step3: 統一建立newFiber
if(oldFiber === null){
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = createChild(returnFiber,newChildren[newIdx],expirationTime,)
// ...
}
}
step4:針對發生移動和更復雜的情況
const existingChildren = mapRemainingChildren(returnFiber, oldFiber); for (; newIdx < newChildren.length; newIdx++) { // 判斷 mapRemainingChildren 中有沒有可以複用 oldFiber ,如果有,那麼複用,如果沒有,新建立一個 newFiber 。 const newFiber = updateFromMap(existingChildren,returnFiber) /* 從mapRemainingChildren刪掉已經複用oldFiber */ }
- mapRemainingChildren返回一個map, 存放剩餘的老fiber和對應的key.
- 遍歷處理剩下的 Children 。
step5: 刪除剩餘沒有複用的oldFiber
if (shouldTrackSideEffects) { // shouldTrackSideEffects 為更新流程。
/* 移除沒有複用到的oldFiber */
existingChildren.forEach(child => deleteChild(returnFiber, child));
}
案例1:節點刪除
- oldChild: A B C D
- newChild: A B
newChild中,A B經過step1 遍歷完成,經過step2 刪除 C D
案例2: 節點增加
- oldChild: A B
- newChild: A B C D
A B 經過 step1遍歷後,oldFiber 沒有可以複用的了,那麼經過step3直接建立 C D。
案例3: 節點位置改變
- oldChild: A B C D
- newChild: A B D C
A B 在第一步被有效複用,step2和step3不符合,通過step4的updateFromMap複用C D。
案例4: 複雜情況(刪除 + 新增 + 移動)
- oldChild: A B C D
- newChild: A E D B
A 節點,在step1 被複用;接下來直接到step4,遍歷 newChild ,E被建立,D B 從 existingChildren 中被複用;existingChildren 還剩一個 C 在step5 被刪除。