两种节点类型我们可以从同级节点的个数把Diff分为两种:当newChild类型为object,number,string时,表示同级只有一个节点;当newChild类型为Array时,同一层有多个节点,在两节中分别讨论这两类节点的Diff。注意这里的单节点是指虚拟dom节点是单节点还是多节点。可以简单的看成返回的数组是单节点还是单节点。比较简单//删除NodefunctiondeleteChild(returnFiber:Fiber,childToDelete:Fiber):void{if(!shouldTrackSideEffects){//Noop.返回;}//效果链的处理constlast=returnFiber.lastEffect;if(last!==null){last.nextEffect=childToDelete;returnFiber.lastEffect=childToDelete;}else{//证明链还没有形成,需要第一个节点returnFiber.firstEffect=returnFiber.lastEffect=childToDelete;}constdeletions=returnFiber.deletions;如果(删除===null){returnFiber.deletions=[childToDelete];//TODO(效果)重命名它以更好地反映其新用法(例如ChildDeletions)returnFiber.effectTag|=Deletion;}else{deletions.push(childToDelete);}childToDelete.nextEffect=null;}//批量删除节点的工具函数(更准确的说,批量标记)functiondeleteRemainingChildren(returnFiber:Fiber,currentFirstChild:Fiber|null,):null{if(!shouldTrackSideEffects){//Noop。返回空值;}//TODO:对于shouldClone的情况,这可以通过//假设在第一个孩子之后我们已经添加了所有内容来进行微优化。让childToDelete=currentFirstChild;while(childToDelete!==null){deleteChild(returnFiber,childToDelete);childToDelete=childToDelete.sibling;它是新的虚拟dom函数reconcileSingleElement(returnFiber:Fiber,currentFirstChild:Fiber|null,element:ReactElement):Fiber{constkey=element.key;让孩子=currentFirstChild;//先判断是否有对应的DOM节点while(child!==null){//上次更新有DOM节点,再判断是否可以重用//先比较key是否相同if(child.key===key){//key相同,则比较类型是否相同switch(child.tag){//...省略大小写default:{if(child.eelementType===element.type){//类型相同表示可以复用deleteRemainingChildren(returnFiber,child.sibling);//显然这个节点的后续节点必须删除,因为constexisting=useFiber(child,element.props);//useFiber,顾名思义,这里的element.props就是后面要调整的属性//return复用的fiberreturnexisting;}//类型不同则跳出switchbreak;}}//代码执行走这里的意思是:key相同但是类型不同//标记fiber和它的siblingfibers为删除deleteRemainingChildren(returnFiber,child);休息;}else{//key不同,标记删除fiberdeleteChild(returnFiber,child);}child=child.sibling;}//新建一个Fiber,并return...Omit}可以发现这里并不会直接删除需要删除的fiber,而是会形成一个effectchain,父节点会维护一个fiber数组ofdeletions先判断子是否存在。如果不存在,则直接开始兄弟节点比较。while在同层比较完成时结束。属性可以是同一个key,但是类型不同,直接deleteRemainingChildren删除这个节点和它的兄弟节点,这里是因为key相同,继续比较查找可复用节点没有意义,所以删除原始节点。关键不一样,直接删除比较节点和多个节点先整理源码函数reconcileChildrenArray(return纤维:纤维,currentFirstChild:纤维|null,newChildren:Array<*>,lanes:车道,):Fiber|null{让resultingFirstChild:纤维|空=空;让以前的NewFiber:纤维|空=空;让oldFiber=currentFirstChild;让lastPlacedIndex=0;让newIdx=0;让nextOldFiber=null;for(;oldFiber!==null&&newIdx=新地图();让existingChild=currentFirstChild;while(existingChild!==null){if(existingChild.key!==null){existingChildren.set(existingChild.key,existingChild);}else{existingChildren.set(existingChild.index,existingChild);}existingChild=existingChild.sibling;}返回现有的孩子;}mapRemainingChildren返回一个由id为key的children组成的Map,或者child的index有更多的内容。一个循环,对比newChildren和oldFiber以及他的兄弟们,会出现三种情况key不一样。循环直接停止。密钥相同,类型不同,纤程被标记为删除。循环继续i++oldFiber=nextOldFiber;循环结束newChildren或oldFiber和他的兄弟遍历结束后,处理几种情况。一种是现在遍历newChildren,然后删除剩下的oldFiber,deleteRemainingChildren(returnFiber,oldFiber);二是oldFiber正在遍历,剩下的newChildren需要创建fiber节点和拼接在previousNewFiber的结果链上触发这两种情况会退出整个diff,也就是还没有遍历完成。这种情况是由节点位置移动引起的。这时,mapRemainingChildren(returnFiber,oldFiber);mapRemainingChildren(returnFiber,oldFiber);map剩余的fibers,然后在Map中查找newChildren剩余的节点,重点是placeChild函数functionplaceChild(newFiber:纤维,lastPlacedIndex:数字,newIndex:数字,):数字{newFiber.index=newIndex;if(!shouldTrackSideEffects){//Noop。返回lastPlacedIndex;}constcurrent=newFiber.alternate;if(current!==null){constoldIndex=current.index;if(oldIndex
