diff算法

avatar
搬砖人

如有错误望大家指出,共同进步

diff算法

一种对比新旧虚拟dom的一种方法, 找出需要更改的虚拟节点并对其进行更新,其他没有更新的则不用改变

使用虚拟dom算法的损耗计算:总损耗 = 虚拟dom增删改+(与diff算法效率有关)真实dom增删改+(较少的节点)排版和重绘

直接操作真实dom的损耗计算:总损耗 = 真实dom完全增删改+(可能较多的节点的)排版和重绘

diff是深度优先算法,进行同层比较

流程:

当数据改变时,会触发setter,并且通过Dep.notify去通知所有订阅者Watcher,订阅者们会调用patch方法,给真实dom打补丁,更新相应的视图

截屏2021-08-08 上午11.49.38.png

vue的diff算法

patch方法:

对比当前同层的虚拟节点是否为同一种类型的标签:

判断其是否是同一类型的标签的标准:

key值是否一样;标签名是否一样;是否都为注释节点;是否都定义了data;当标签是input时,type是否相同;

  • 是:继续执行patchNode方法:

    • 找到对应的真实dom,称为el

    • 判断newNode和oldnode是否指向同一个对象,如果是,直接return

    • 如果他们都有文本节点并且不相等,那么直接将el的文本节点设置为newVnode的文本节点

    • 如果oldVnode有子节点而newVnode没有,则删除el的子节点

    • 如果oldVnode没有子节点而newVnode有,则将newVnode的子节点真实化之后添加到el

    • 如果两者都有子节点,则执行updateChildren函数比较子节点

      updateChildren方法:新的子节点集合和旧的子节点的集合各有首尾两个指针

      新旧节点首尾进行四次比较,如果四次都匹配不到,则将所有旧子节点的key做一个映射到旧节点下标的key=>index表,然后用新vnode的key去找出旧节点中可以复用的位置

  • 否:没必要比对,直接整个节点替换成新虚拟节点

vue2与vue3的对比

  • vue2中会将没有发生更改的节点进行比较,比较消耗性能;在vue3中,创建虚拟dom树的时候,会根据dom中的内容会不会发生变化添加静态标记,在之后的过程中比较这些带有静态标记的节点。

摘自原文blog.csdn.net/qq_45613931…

export const enum PatchFlags {
  // 动态文本节点
  TEXT = 1,
  // 动态 class
  CLASS = 1 << 1, // 2
  // 动态 style
  STYLE = 1 << 2, // 4
  // 动态属性,但不包含类名和样式
  // 如果是组件,则可以包含类名和样式
  PROPS = 1 << 3, // 8
  // 具有动态 key 属性,当 key 改变时,需要进行完整的 diff 比较。
  FULL_PROPS = 1 << 4, // 16
  // 带有监听事件的节点
  HYDRATE_EVENTS = 1 << 5, // 32
  // 一个不会改变子节点顺序的 fragment
  STABLE_FRAGMENT = 1 << 6, // 64
  // 带有 key 属性的 fragment 或部分子字节有 key
  KEYED_FRAGMENT = 1 << 7, // 128
  // 子节点没有 key 的 fragment
  UNKEYED_FRAGMENT = 1 << 8, // 256
  // 一个节点只会进行非 props 比较
  NEED_PATCH = 1 << 9, // 512
  // 动态 slot
  DYNAMIC_SLOTS = 1 << 10, // 1024
  // 静态节点
  HOISTED = -1,
  // 指示在 diff 过程应该要退出优化模式
  BAIL = -2
} 
  • vue3中存在最长递增子序列使得我们可以保证移动次数最少

react的diff算法与vue的区别

  • react在diff遍历的时候,只对需要修改的节点进行了记录,形成effect list,最后才会根据effect list进行真实的dom的修改,修改时先删除,然后更新和移动,最后插入
  • vue在遍历的时候修改真实dom最后做删除操作
  • react采用单指针从左到右进行遍历,vue采用双指针从两头向中间进行遍历

用index作为key的问题

vue中使用虚拟dom且根据diff算法进行新旧对比,从而更新真实dom,key是虚拟dom对象的唯一标识。

如果旧虚拟dom中找到了与新虚拟dom相同的key:

  • 若虚拟dom中的内容没变,直接使用之前的真实dom
  • 若虚拟dom中内容变了,则生成新的真实dom,随后替换掉页面中之前的真实dom

若对数据进行逆序添加,逆序删除等破i坏顺序操作:会产生没有必要的真实dom更新,页面效果没问题,但是效率低 如果结构中还包含输入类的dom:会产生错误dom更新=》界面有问题

如果不存在对数据的逆序添加或删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的 都会根据老的节点构建一个map,方便根据key快速查找

本文参考资料链接: juejin.cn/post/699495… juejin.cn/post/697837…