2021年02月05日(Diff)


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>
    1. newChildren与oldFiber都遍历完如果都遍历完,说明节点复用了,因为在遍历时如果key不同则会跳出遍历,如果key相同,type不同,则会为oldFiber标记为DELETION,并继续遍历
    2. newChildren没遍历完,oldFiber遍历完,这个时候该复用的已经复用,,但是newChildren没有遍历完,说明会有新增的节点
    3. newChildren遍历完,oldFiber没遍历完,意味着本次更新比之前的节点数量少,有节点被删除了。所以需要遍历剩下的oldFiber,依次标记Deletion

对于只是移动的节点,可以通过key来判断,如何判断节点是否移动?

需要一个参照物,最后一个可复用的节点在oldFiber中的位置索引(用变量lastPlacedIndex表示)。

由于本次更新中节点是按newChildren的顺序排列。在遍历newChildren过程中,每个遍历到的可复用节点一定是当前遍历到的所有可复用节点最靠右的那个,即一定在lastPlacedIndex对应的可复用的节点在本次更新中位置的后面。

那么我们只需要比较遍历到的可复用节点在上次更新时是否也在lastPlacedIndex对应的oldFiber后面,就能知道两次更新中这两个节点的相对位置改变没有。

如果oldIndex < lastPlacedIndex,代表本次更新该节点需要向右移动。

lastPlacedIndex初始为0,每遍历一个可复用的节点,如果oldIndex >= lastPlacedIndex,则lastPlacedIndex = oldIndex


Author: wxy
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source wxy !
  TOC