frontend-notes

Diff算法

React diff - 递增法

Vue2.x diff - 双端比较

Vue3.x diff - 最长递增子序列

  1. 前置与后置元素的预处理
    • 首先从 newList 头节点开始比较,遇到相同的节点指针向内移动,否则跳出循环
    • 从 newList 尾节点开始比较,遇到相同节点指针向内移动,否则跳出循环
  2. 剩余的节点就是不确定元素了,使用最长递增子序列进行节点移动、新增或删除

最长递增子序列

经过前置与后置元素预处理后就可以计算出剩余的节点的最长递增子序列了,具体操作如下

  1. 先根据新列表剩余的节点数量,创建一个source数组,并将数组填满-1。
     DOM        A   B   C   D   F   E
                    ↓           ↓
     prevList   a   b   c   d   f   e
    
     newList    a   c   d   b   g   e
                    ↑           ↑
     source       [-1  -1  -1  -1]
    
  2. 然后根据新节点在旧列表的位置存储在该数组中,如果旧节点在新列表中没有的话,直接删除(如F),对于全新的节点,其在source中的值为-1,通过这一步我们可以区分出来哪个为全新的节点,哪个是可复用的。
     DOM        A   B   C   D [-F]  E
                    ↓           ↓
     prevList   a   b   c   d   f   e
    
     newList    a   c   d   b   g   e
                    ↑           ↑
     source       [ 2   3   1  -1]
    
  3. 计算出source的最长递增子序列
     DOM        A   B   C   D [-F]  E
                    ↓           ↓
     prevList   a   b   c   d   f   e
    
     newList    a   c   d   b   g   e
                    ↑           ↑
     source       [ 2   3   1  -1]
     Lis          [ 0   1 ]
    
  4. 从后向前进行遍历source每一项,此时会出现三种情况:
    • 当前的值为-1,这说明该节点是全新的节点,又由于我们是从后向前遍历,我们直接创建好DOM节点插入到队尾就可以了。
    • 当前的索引为最长递增子序列中的值,也就是i === seq[j],这说说明该节点不需要移动
    • 当前的索引不是最长递增子序列中的值,那么说明该DOM节点需要移动,这里也很好理解,我们也是直接将DOM节点插入到队尾就可以了,因为队尾是排好序的。 ``` ↗¯¯¯¯¯¯¯¯¯¯↘ DOM A [-B] C D [+B] [+G] E ↓ ↓ prevList a b c d f e

    newList a c d b g e ↑ ↑ source [ 2 3 1 -1] Lis [ 0 1 ] ```

参考