Vue diff简介

Vue3 diff

最长递增子序列+双端diff

理念

  1. 相同的前置和后置元素的预处理,考虑边界情况,减少移动;
  2. 最长递增子序列(动态规划、二分法),判断是否需要移动

操作

  1. 前置与后置预处理
  2. 判断是否需要移动
    递增法:根据新列表剩余的节点数,创建一个source数组,填入-1,存储新节点在旧节点的位置,再算出source中最长递增子序列,用于移动DOM,如旧节点在新列表中没有,则直接删除
  3. 移动DOM:根据source对新列表进行重新编号,找到source的最长递增子序列在原本数组中的索引,创建一个数组保存每个值的最长子序列在数组中的index,然后从后向前遍历source
    • 当前index=-1,则创建DOM节点插入队尾
    • 当前index为最长递增子序列中的值,则不需要移动,判断是否有全新的节点添加
    • 当前index不在最长递增子序列中,则移动,将DOM节点插入队尾

细节

  1. 同层级比较
  2. 节点类型判断:不同类型直接替换
  3. key值比较:相同key可复用
  4. 属性比较:只更新变化的属性

Vue2 diff

双端比较

缺乏事件切片能力,除了高帧率动画,其他场景都可以使用防抖和节流提高响应性能

理念

新列表和旧列表两个列表的头和尾互相对比,在对比的过程中指针会逐渐向内靠拢,直到某一个列表的节点全部遍历过,对比停止

过程

  1. vue的diff算法是平级比较,不考虑跨级比较的情况,即只有在新旧children都为多个子节点时才需要使用diff算法进行同层级比较
  2. 内部采用深度递归的方式+双指针的方式进行比较
    • 旧children和新children各有两个头尾的变量startIdx和EndIdx,头头、尾尾、头尾、尾头进行对比,一共四种比较方式
      • 使用旧列表的头一个节点与新列表的头一个节点对比
      • 使用旧列表的最后一个节点与新列表的最后一个节点对比
      • 使用旧列表的头一个节点与新列表的最后一个节点对比
      • 使用旧列表的最后一个节点与新列表的头一个节点对比去
        寻找key相同的可复用的节点,如果找到则停止后面的寻找;
    • 四种方式都没匹配,如果设置了key,就用key进行比较,拿新列表中第一个节点去旧列表中找与其key相同的节点
      • 在旧列表中找到了:移动找到的节点,旧列表中的节点改为undefined,移动过的节点不需要再进行节点对比;
      • 在旧列表中没找到:创建新节点放到最前面在比较中,变量会往中间靠,如果startIdx>EndIdx,表明旧children和新children至少有一个已经遍历完成,结束比较
  3. vue的diff算法称为patching算法,diff执行时刻是组件内响应式数据变更触发实例执行其更新函数,再次执行render函数得到最新的虚拟DOM,然后执行patch函数,并传入新旧两次的虚拟DOM,对比找到变化,将其转化为对应的DOM操作
  4. patch过程是递归过程,遵循深度优先,同层比较的策略

Vue3 diff和Vue2 diff比较

  1. 最长递增子序列算法
    减少不必要的DOM操作,提升性能
  2. 静态标记
    更新时跳过静态节点
  3. 缓存数组
    将新旧VNode数组缓存,只对数组中不同的VNode对比,减少比对次数,提升性能
  4. 动态删除数组
    异步队列方式,将多个删除操作合并