Vue2和Vue3和React三者的diff算法有什么区别?

1,033 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

Vue2和Vue3和React三者的diff算法有什么区别?

diff 算法是 vue2.x , vue3.x 以及 react 中关键核心点,三者的diff又有什么区别,这个问题是在社区里面讨论很多的话题,面试的时候也经常遇到,本节希望可以用比较通俗易懂的语言解释一下这三者中最大的区别。

diff 算法

其实我们之前也有说过不论 Vue、React数据更新之后,重新生成 VDOM,然后对虚拟DOM进行对比,然后找出需要更新的节点。相信很多人了解 diff 算法也是在这些框架中了解的吧!

但是!但是 diff 算法其实是一个非常普遍常用的方法,例如提交github pr 或者(gitlab mr)时,会对比当前提交代码的改动,这就是 diff

Vue、React 的 diff 不是对比文字,而是 vdom 树,即 tree diff 。
传统的 tree diff 算法复杂度是 O(n^3) ,算法不可用。 既然不可用那怎么办呢?那就进行优化!

tree diff优化

传统的 tree diff 算法复杂度是 O(n^3),不可用。

tree diff优化的关键就是只对比同层的节点,而不是跨层对比,这也是考虑到在实际业务中很少会去跨层的移动 DOM 元素。

Vue React 都是用于网页开发,基于 DOM 结构,对 diff 算法都进行了优化(或者简化)

  1. 只在同一层级比较,不跨层级
    • 在网页DOM树中,很少会有跨级别的移动,一般都是同级别的删除、移动。
  2. tag 不同则直接删掉重建,不去对比内部细节
    • 和上一点差不多一个意思,一旦发现不同,不深入进行比较。
    • 对DOM类型的元素来说,key 和 tag 都相同才会复用。
  3. 同一个节点下的子节点,通过 key 区分
    • 这个不论我们在写 Vue 还是 React的循环的时候,都需要有一个key,不然的话就会收到警告或者报错

通过这么三个小点的优化之后,最终把时间复杂度降低到 O(n) ,生产环境下可用。这一点 Vue React 都是相同的。

从左边的树到右边的树,用我们优化之后的diff算法会这么表现? tree-diff-1.png

  • 上图实例中
  • 灰色表示没有发生变化的节点
  • 红色表示被删除的节点
  • 绿色表示新增的节点 我们可以发现 root、b并没有被删除,a和c节点同节点比较的时候发现没有,直接进行删除,然后新增a和c节点。

Vue2和Vue3和React三者的diff的特点分别是?

React diff 特点

  • 仅向右移动 我们来举一个简单的例子:

源数据: a b c d e
目标数据: b c a d e 目标是:按照React diff 特点(仅向右移动)模拟dom更新的过程 react-diff.png

  1. 首先我们发现目标数据b和源数据的位置不一致
    • 正常的话交换位置,我们会将源数据的b左移动和a进行交换位置
    • 但是遵循仅向右移动原则,我们不能进行移动,然后往后走
  2. 我们发现目标数据c和源数据的位置不一致
    • 但是遵循仅向右移动原则,我们不能进行移动,继续往后走
  3. 接着我们到了目标 a 的位置
    • 我们发现源数据的a可以遵循仅向右移动原则
  4. 当a移动到新位置之后,b和c就自然左移,这里就完成了,我们的目标数据的转换了。

Vue2 diff 特点

vue2 的 diff 位于Vue源码的 patch.js 文件中,该算法来源于 snabbdom,复杂度为 O(n)。

  • 特点:双端比较
    • 同时从新旧 children 的两端开始进行比较,借助 key 可以复用的节点。
    • 定义四个指针(oldStartNode, oldEndNode, newStartNode, newEndNode),两两进行比较,之后每移动一次,都会进行一次比较,知道移动到中间的时候相遇了
      • oldStartNode 和 newStartNode
      • oldStartNode 和 newEndNode
      • oldEndNode 和 newStartNode
      • oldEndNode 和 newEndNode

Vue3 diff 特点

  1. 处理相同的前置和后置元素的预处理; 2.一旦需要进行 DOM 移动,我们首先要做的就是找到 source 的最长递增子序列。 该方法旨在尽量减少 DOM 的移动,达到最少的 DOM 操作。

Vue和React循环中为什么都需要key?

  • vdom diff算法中会根据key判断元素是否要删除
  • 匹配了key
    • 在vue中:借助 key 可以复用的节点。
    • react 通过 key 和 tag 来对节点进行取舍
  • 没有匹配key
    • 那么可以重复使用的元素则是选择删除重建,过程中需要重新进行渲染,会消耗一部分性能。

总结

  • diff 算法
    • diff算法很早就有
    • diff算法应用很广泛,例如提交github pr 或者(gitlab mr
    • 如果要严格diff两棵树,时间复杂度是 O(n^3) ,算法不可用。
  • tree diff优化
    • 之比较同一层级,不跨级比较
    • tag 不同则删掉重建(不再去比较内部的细节)
  • React diff 特点
    • 仅向右移动
  • Vue2 diff 特点
    • 时从新旧 children 的两端开始进行比较,借助 key 可以复用的节点。
    • 定义四个指针(oldStartNode, oldEndNode, newStartNode, newEndNode),两两进行比较,之后每移动一次,都会进行一次比较,知道移动到中间的时候相遇了
  • Vue3 diff 特点
    • 最长递增子序列

推荐

【1】diff算法深入一下?

往期

【1】从源码设计看 Vue3 的性能提升

【2】Vue.js - 响应式、Diff算法