大体流程
1 按层对比,如果不相同则直接重新创建
2 如果是数组,则对比前后形成的数组,按照一定的规则对比
数组diff
1 首先遍历新数组,如果旧数组和新数组的index相同的情况下,如果key相同,则利用旧数组的元素,形成的新的元素。如果key不同,则直接跳出循环,到第4步
2 如果新数组遍历完,还有老数组的元素存在,则删除
3 如果老数组遍历结束之后,新数组还存在,则直接把新数组的元素都插入
4 开始第二次循环,首先把老数组中的元素按照key形成hash表和一个exist数组,遍历新数组,如果新数组中的元素的key在hash表存在的话,就利用hash表中对应的值形成新的元素,同时在exist数组中删除
5 结果得到一个打上各种tag的数组
// returnFiber表示父元素fiber,currentFirstChild 表示以前的数组中的第一个元素
// newChildren 表示当前形成的新数组,expirationTime 时间片
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, expirationTime) {
// This algorithm can't optimize by searching from both ends since we
// don't have backpointers on fibers. I'm trying to see how far we can get
// with that model. If it ends up not being worth the tradeoffs, we can
// add it later.
// Even with a two ended optimization, we'd want to optimize for the case
// where there are few changes and brute force the comparison instead of
// going for the Map. It'd like to explore hitting that path first in
// forward-only mode and only go for the Map once we notice that we need
// lots of look ahead. This doesn't handle reversal as well as two ended
// search but that's unusual. Besides, for the two ended optimization to
// work on Iterables, we'd need to copy the whole set.
// In this first iteration, we'll just live with hitting the bad case
// (adding everything to a Map) in for every insert/move.
// If you change this code, also update reconcileChildrenIterator() which
// uses the same algorithm.
{
// First, validate keys.
var knownKeys = null;
for (var i = 0; i < newChildren.length; i++) {
var child = newChildren[i];
knownKeys = warnOnInvalidKey(child, knownKeys);
}
}
var resultingFirstChild = null;
var previousNewFiber = null;
var oldFiber = currentFirstChild;
var lastPlacedIndex = 0;
var newIdx = 0;
var nextOldFiber = null;
// 这里的oldFiber是指在旧的数组中的第一个元素,首先依次遍历新数组,首先看index相同的元素是否key也相同
// 如果相同,就使用老元素生成新元素
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
// 暂时没发现什么情况下出现这种情况
if (oldFiber.index > newIdx) {
nextOldFiber = oldFiber;
oldFiber = null;
} else {
nextOldFiber = oldFiber.sibling;
}
// 如果key相同,则根据oldFiber 生成 newFiber
var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], expirationTime);
if (newFiber === null) {
// TODO: This breaks on empty slots like null children. That's
// unfortunate because it triggers the slow path all the time. We need
// a better way to communicate whether this was a miss or null,
// boolean, undefined, etc.
if (oldFiber === null) {
oldFiber = nextOldFiber;
}
break;
}
if (shouldTrackSideEffects) {
if (oldFiber && newFiber.alternate === null) {
// We matched the slot, but we didn't reuse the existing fiber, so we
// need to delete the existing child.
deleteChild(returnFiber, oldFiber);
}
}
// 在旧的数组中,lastPlacedIndex表示旧数组中的所有已经在新数组中更新好了的元素的最大的位置,所以数组
// 中如果有元素的索引小于lastPlacedIndex,所以该元素在新数组中的索引肯定和旧数组中是不一样的,肯定是需要
// 移动顺序的。
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
// TODO: Move out of the loop. This only happens for the first run.
resultingFirstChild = newFiber;
} else {
// TODO: Defer siblings if we're not at the right index for this slot.
// I.e. if we had null values before, then we want to defer this
// for each null value. However, we also don't want to call updateSlot
// with the previous one.
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
oldFiber = nextOldFiber;
}
// 遍历完毕新数组了,把老数组中的剩下的元素删除
if (newIdx === newChildren.length) {
// We've reached the end of the new children. We can delete the rest.
deleteRemainingChildren(returnFiber, oldFiber);
return resultingFirstChild;
}
// 如果遍历完了老数组了,但是新数组还有,则剩下的都插入
if (oldFiber === null) {
// If we don't have any more existing children we can choose a fast path
// since the rest will all be insertions.
for (; newIdx < newChildren.length; newIdx++) {
var _newFiber = createChild(returnFiber, newChildren[newIdx], expirationTime);
if (!_newFiber) {
continue;
}
lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
// TODO: Move out of the loop. This only happens for the first run.
resultingFirstChild = _newFiber;
} else {
previousNewFiber.sibling = _newFiber;
}
previousNewFiber = _newFiber;
}
return resultingFirstChild;
}
// Add all children to a key map for quick lookups.
// 把老数组中按照key形成hash表
var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
// Keep scanning and use the map to restore deleted items as moves.
// 然后再新数组中查找,如果新数组中的key在老数组中就利用老数组中的元素,更新新数组中的元素
for (; newIdx < newChildren.length; newIdx++) {
var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], expirationTime);
if (_newFiber2) {
if (shouldTrackSideEffects) {
if (_newFiber2.alternate !== null) {
// The new fiber is a work in progress, but if there exists a
// current, that means that we reused the fiber. We need to delete
// it from the child list so that we don't add it to the deletion
// list.
existingChildren.delete(_newFiber2.key === null ? newIdx : _newFiber2.key);
}
}
lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = _newFiber2;
} else {
previousNewFiber.sibling = _newFiber2;
}
previousNewFiber = _newFiber2;
}
}
if (shouldTrackSideEffects) {
// Any existing children that weren't consumed above were deleted. We need
// to add them to the deletion list.
existingChildren.forEach(function (child) {
return deleteChild(returnFiber, child);
});
}
return resultingFirstChild;
}
例子
1 [A,B,C,D,E] =>[A,F,B,C,D]
步骤
1 首先遍历新数组,如果旧数组和新数组的index相同的情况下,如果key相同,则利用旧数组的元素,形成的新的元素。如果key不同,则直接跳出循环,到第4步
这里到看到B和F两者的key是不一样的,所以跳出循环,到第4步
2 如果新数组遍历完,还有老数组的元素存在,则删除
3 如果老数组遍历结束之后,新数组还存在,则直接把新数组的元素都插入
4 开始第二次循环,首先把老数组中的元素按照key形成hash表和一个exist数组,遍历新数组,如果新数组中的元素的key在hash表存在的话,就利用hash表中对应的值形成新的元素,同时在exist数组中删除
4.1 hash = {
B:B,
C:C,
D,D,
E,E
}
exit = [B,C,D,E]
lastPlaceIndex = 0;lastPlaceIndex是指在旧数组中已经更新到新数组中元素中索引最大的值,所以这里是0
oldIndex=undefined,oldIndex是指当前旧数组中遍历到元素的索引
newIndex=1,newIndex是指当前新数组中遍历到元素的索引
4.2 newIndex = 1;新数组中是F,可以发现在hash中不存在,则打上插入的tag,lastPlaceIndex=0不变
4.3 newIndex =2,新数组中是B,在hash中存在,则oldIndex=1,因为oldIndex > lastPlaceIndex,则不发生变化,同时lastPlaceIndex =1,同时exit=[C,D,E]
4.4 newIndex =3,新数组中是C,在hash中存在,则oldIndex=2,因为oldIndex > lastPlaceIndex,则不发生变化,同时lastPlaceIndex =2,同时exit=[D,E]
4.5 依次类推,最后直接删除E
5 结果得到一个打上各种tag的数组