# 浅学习一下 diff 算法
# 什么是 diff 算法?
Diff 算法是一种对比算法。对比两者是旧虚拟 DOM 和新虚拟 DOM,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实 DOM,进而提高效率。
# diff 算法做了什么?
- 当数据改变时,会触发 setter,并且通过 Dep.notify 去通知所有订阅者 Watcher,订阅者们就会调用 patch 方法,给真实 DOM 打补丁,更新相应的视图。
- patch 方法的作用就是,对比当前同层的虚拟节点是否为同一种类型的标签,是:继续执行 patchVnode 方法进行深层比对;否:没必要比对了,直接整个节点替换成新虚拟节点
- patch 中用 sameVnode 方法和 patchVnode 方法判断是否为同一类型节点(key 值一样,标签一样,是否都为注释,是否都定义了 data),如果不是同一节点,则直接替换
- key 值是否一样?标签名是否一样?是否都为注释节点?是否都定义了 data?当标签为 input 时,type 必须是否相同?
- patchVnode 方法
- 找到对应的真实 DOM,称为 el
- 判断 newVnode 和 oldVnode 是否指向同一个对象,如果是,那么直接 return
- 如果他们都有文本节点并且不相等,那么将 el 的文本节点设置为 newVnode 的文本节点。
- 如果 oldVnode 有子节点而 newVnode 没有,则删除 el 的子节点
- 如果 oldVnode 没有子节点而 newVnode 有,则将 newVnode 的子节点真实化之后添加到 el
- 如果两者都有子节点,则执行 updateChildren 函数比较子节点
# 使用 diff 的好处?
性能大大提升,减少频繁操作节点带来的性能损耗
使用虚拟 DOM 算法的损耗计算: 总损耗 = 虚拟 DOM 增删改+(与 Diff 算法效率有关)真实 DOM 差异增删改+(较少的节点)排版与重绘
直接操作真实 DOM 的损耗计算: 总损耗 = 真实 DOM 完全增删改+(可能较多的节点)排版与重绘
新旧虚拟 DOM 对比的时候,Diff 算法比较只会在同层级进行, 不会跨层级比较。 所以 Diff 算法是:深度优先算法。 时间复杂度:O(n)
# 为什么不能用index
作为循环的 key 值呢?
- Diff 算法借助元素的 Key 判断元素是新增、删除、修改,从而减少不必要的元素重渲染。
- 当列表第一项插入一个元素时,key 值为 0,之后的元素的 key 会依次变更,从而全部更新,而原先最后一个元素由于 key 值为原来的
index+1
,会被认为是新增的,导致前面元素数据的绑定不正确