if(currentFirstChild !==null&& currentFirstChild.tag === HostText){// We already have an existing node so let's just update it and delete// the rest.deleteRemainingChildren(returnFiber, currentFirstChild.sibling);// 删除兄弟const existing =useFiber(currentFirstChild, textContent, expirationTime); existing.return = returnFiber;return existing;// 复用}
// The existing first child is not a text node so we need to create one// and delete the existing ones.// 创建新的Fiber节点,将旧的节点和旧节点的兄弟都删除 deleteRemainingChildren(returnFiber, currentFirstChild);const created =createFiberFromText( textContent, returnFiber.mode, expirationTime,);
// react-reconciler/src/ReactChildFiber.js line 1132const key = element.key;let child = currentFirstChild;while(child !==null){// TODO: If key === null and child.key === null, then this only applies to// the first item in the list.if(child.key === key){if( child.tag === Fragment
? element.type ===REACT_FRAGMENT_TYPE: child.elementType === element.type ||// Keep this check inline so it only runs on the false path:(__DEV__
?isCompatibleFamilyForHotReloading(child, element):false)){deleteRemainingChildren(returnFiber, child.sibling);// 因为当前节点是只有一个节点,而老的如果是有兄弟节点是要删除的,是多余的const existing =useFiber( child, element.type ===REACT_FRAGMENT_TYPE? element.props.children
: element.props, expirationTime,); existing.ref =coerceRef(returnFiber, child, element); existing.return = returnFiber;// ...return existing;}else{deleteRemainingChildren(returnFiber, child);break;}}else{deleteChild(returnFiber, child);// 从child开始delete} child = child.sibling;// 继续从子节点中找到一个可复用的节点}
// react-reconciler/src/ReactChildFiber.js line 756functionreconcileChildrenArray(returnFiber: Fiber,currentFirstChild: Fiber |null,newChildren: Array<*>,expirationTime: ExpirationTime,): Fiber |null{// 机翻注释// 这个算法不能通过两端搜索来优化,因为我们在光纤上没有反向指针。我想看看我们能用这个模型走多远。如果最终不值得权衡,我们可以稍后再添加。// 即使是双端优化,我们也希望在很少有变化的情况下进行优化,并强制进行比较,而不是去寻找地图。它想探索在前进模式下首先到达那条路径,并且只有当我们注意到我们需要很多向前看的时候才去地图。这不能处理反转以及两个结束的搜索,但这是不寻常的。此外,要使两端优化在Iterables上工作,我们需要复制整个集合。// 在第一次迭代中,我们只需在每次插入/移动时都碰到坏情况(将所有内容添加到映射中)。// 如果更改此代码,还需要更新reconcileChildrenIterator(),它使用相同的算法。let oldFiber = currentFirstChild;let lastPlacedIndex =0;let newIdx =0;let nextOldFiber =null;// 第一个for循环,按照index一一对比,当新老节点不一致时退出循环并且记录退出时的节点及oldFiber节点for(; oldFiber !==null&& newIdx < newChildren.length; newIdx++){if(oldFiber.index > newIdx){// 位置不匹配 nextOldFiber = oldFiber;// 下一个即将对比的旧节点 oldFiber =null;// 如果newFiber也为null(不能复用)就退出当前一一对比的for循环}else{ nextOldFiber = oldFiber.sibling;//正常的情况下 为了下轮循环,拿到兄弟节点下面赋值给oldFiber}// //如果节点可以复用(key值匹配),就更新并且返回新节点,否则返回为null,代表节点不可以复用const 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 =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;// 重新给 oldFiber 赋值继续遍历}
// react-reconciler/src/ReactChildFiber.js line 839// 新节点已经更新完成,删除多余的老节点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++){const newFiber =createChild( returnFiber, newChildren[newIdx], expirationTime,);if(newFiber ===null){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;}
// react-reconciler/src/ReactChildFiber.js line 872// Add all children to a key map for quick lookups.// 从oldFiber开始将已经存在的节点的key或者index添加到map结构中const existingChildren =mapRemainingChildren(returnFiber, oldFiber);// Keep scanning and use the map to restore deleted items as moves.// 剩余没有对比的新节点,到旧节点的map中通过key或者index一一对比查看是否可以复用。for(; newIdx < newChildren.length; newIdx++){// 主要查看新旧节点的key或者index是否有相同的,然后再查看是否可以复用。const newFiber =updateFromMap( existingChildren, returnFiber, newIdx, newChildren[newIdx], expirationTime,);if(newFiber !==null){if(shouldTrackSideEffects){if(newFiber.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(// 在map中删除掉已经复用的节点的key或者index newFiber.key ===null? newIdx : newFiber.key,);}} lastPlacedIndex =placeChild(newFiber, lastPlacedIndex, newIdx);// 添加newFiber到更新过的newFiber结构中。if(previousNewFiber ===null){ resultingFirstChild = newFiber;}else{ previousNewFiber.sibling = newFiber;} previousNewFiber = newFiber;}}// react-reconciler/src/ReactChildFiber.js line 299// 将旧节点的key或者index,旧节点保存到map结构中,方便通过key或者index获取functionmapRemainingChildren(returnFiber: Fiber,currentFirstChild: Fiber,): Map<string | number, Fiber>{// Add the remaining children to a temporary map so that we can find them by// keys quickly. Implicit (null) keys get added to this set with their index// instead.constexistingChildren: Map<string | number, Fiber>=newMap();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;}