React
1.在react
中最多会存在两颗Fiber
树,视图中的Fiber
树(current Fiber),和内存中构建的Fiber
树(workInProgress Fiber)
react
应用的根节点会通过current
指针,在这两颗Fiber
树之间进行切换,当workInProgress Fiber
构建完成时渲染在页面上之后,current
指针就会指向workInProgress Fiber
,它就变成了current Fiber
在update时,会生成一棵新的workInProgress Fiber
,他的创建可以复用current Fiber
对应的节点数据,这个过程称之为Diff
2.如何判断dom节点是否可以复用?
单节点:
只需判断key和type即可,如果key不相同,type就可以不用看了,如果key相同,但是type不同,也不可以复用,key最好设置为id,如果是index,其实和没设置是一样的
多节点:
Diff
其实就是增,删,改,不同的操作是有优先级的,因为更新的频率更高,所以Diff
会优先判断当前节点属于是不是更新操作Diff算法
的整体逻辑会经历两轮遍历:第一轮遍历:处理
更新
的节点。第二轮遍历:处理剩下的不属于
更新
的节点。// 之前 <li key="0" className="a">0</li> <li key="1" className="b">1</li> // 之后 情况1 —— newChildren与oldFiber都遍历完 <li key="0" className="aa">0</li> <li key="1" className="bb">1</li> // 之后 情况2 —— newChildren没遍历完,oldFiber遍历完 // newChildren剩下 key==="2" 未遍历 <li key="0" className="aa">0</li> <li key="1" className="bb">1</li> <li key="2" className="cc">2</li> // 之后 情况3 —— newChildren遍历完,oldFiber没遍历完 // oldFiber剩下 key==="1" 未遍历 <li key="0" className="aa">0</li>
- newChildren与oldFiber都遍历完如果都遍历完,说明节点复用了,因为在遍历时如果key不同则会跳出遍历,如果key相同,type不同,则会为oldFiber
标记为
DELETION,并继续遍历 - newChildren没遍历完,oldFiber遍历完,这个时候该复用的已经复用,,但是newChildren没有遍历完,说明会有新增的节点
- newChildren遍历完,oldFiber没遍历完,意味着本次更新比之前的节点数量少,有节点被删除了。所以需要遍历剩下的
oldFiber
,依次标记Deletion
- newChildren与oldFiber都遍历完如果都遍历完,说明节点复用了,因为在遍历时如果key不同则会跳出遍历,如果key相同,type不同,则会为oldFiber
对于只是移动的节点,可以通过key来判断,如何判断节点是否移动?
需要一个参照物,最后一个可复用的节点在oldFiber
中的位置索引(用变量lastPlacedIndex
表示)。
由于本次更新中节点是按newChildren
的顺序排列。在遍历newChildren
过程中,每个遍历到的可复用节点
一定是当前遍历到的所有可复用节点
中最靠右的那个,即一定在lastPlacedIndex
对应的可复用的节点
在本次更新中位置的后面。
那么我们只需要比较遍历到的可复用节点
在上次更新时是否也在lastPlacedIndex
对应的oldFiber
后面,就能知道两次更新中这两个节点的相对位置改变没有。
如果oldIndex < lastPlacedIndex
,代表本次更新该节点需要向右移动。
lastPlacedIndex初始为
0,每遍历一个可复用的节点,如果
oldIndex >= lastPlacedIndex,则
lastPlacedIndex = oldIndex