面试的时候经常会有面试官问到,vue diff 和 react diff,如果你vue 和 react 都会,那么恭喜可能会中枪,面试官会让你先说vue2和vue3的数据双向绑定,数据劫持的不同,再说vue的key的作用,引出vue的diff,再到react的diff,在说出他们的区别,在给你一个例子,各自怎么移动的
⚙️ 1. 节点比较策略:双端 vs 单向
- Vue(双端比较)
采用首尾双指针 同时向中间移动,优先对比头部/尾部节点(头-头、尾-尾、头-尾、尾-头)。若匹配成功则复用节点并移动指针;若无匹配,则基于 最长递增子序列(LIS) 算法处理中间节点的移动。
优势 :对节点位置变化(如列表重排)更高效。例如,将末尾节点移到开头时,Vue 只需移动一个节点。 - React(单向遍历)
采用从左到右的单指针 遍历,按顺序对比新旧节点。依赖key
和lastIndex
判断复用:若新节点index < lastIndex
,则标记移动。
劣势 :末尾节点移到开头时,需将前方所有节点右移,导致 O(n) 移动开销。
💡 示例场景 :列表
[A, B, C, D]
→[D, A, B, C]
- Vue:移动
D
到开头(1 次操作)。- React:将
A/B/C
依次右移(3 次操作)。
🧩 2. 节点复用逻辑:属性敏感性 vs 类型优先
- Vue
若节点标签类型相同但关键属性(如className
)不同 ,视为不同节点,删除并重建 。例如<div class="a">
变为<div class="b">
会触发重建。
原因:Vue 认为属性变化可能影响布局,需彻底更新。 - React
只要标签类型和key
相同 ,仅更新变化的属性 (如修改className
),不重建节点。
优势:减少 DOM 操作,但对样式变化敏感的场景可能需手动优化。
⚡ 3. 更新机制:同步 vs 批量
- Vue
在 Diff 过程中同步更新真实 DOM (如调用insertBefore
),最后处理删除。
影响:更早呈现变化,但频繁更新可能引发布局抖动。 - React
先收集变更形成effect list
,再批量更新 DOM (先删除 → 更新 → 插入)。结合 Fiber 架构支持中断/恢复更新,适合高优先级任务。
🚀 4. 优化策略:编译时 vs 运行时
-
Vue 3 的编译时优化
- 静态提升(Hoisting) :编译阶段标记静态节点,跳过运行时 Diff。
- Patch Flags:动态节点标记变化类型(如文本/属性),仅对比标记部分。
- LIS 算法:最小化节点移动次数。
-
React 的运行时优化
- Fiber 时间分片:将 Diff 拆分为可中断的微任务,避免阻塞主线程。
- 手动优化 :依赖开发者通过
shouldComponentUpdate
或React.memo
避免无效更新。
🔍 5. 核心差异总结
下表对比两者根本区别:
维度 | Vue | React |
---|---|---|
比较方向 | 双端(两端→中间) | 单向(左→右) |
节点复用 | 属性变化则重建 | 同类型节点仅更新属性 |
列表移动优化 | LIS 算法,移动次数最少 | 基于 index/lastIndex,移动开销更大 |
DOM 更新时机 | Diff 中同步更新 | 批量更新(effect list) |
性能侧重 | 复杂列表动态更新 | 静态结构+手动优化 |
💎 根本结论
Vue 和 React Diff 算法的最根本区别 在于:
👉 Vue 通过双端比较 + LIS 算法 + 编译时优化,优先减少节点移动次数,尤其擅长动态列表更新 ;
👉 React 依赖单向遍历 + Fiber 分片,强调批量更新和开发者手动控制,适合结构稳定的应用。
选择建议:
- 高频列表操作(如拖拽排序)选 Vue;
- 需精细控制更新逻辑或集成复杂状态管理选 React。
如果你觉得上面太简洁,同样也可以这样说
首先,两者有一些共同点:都只进行同级比较而不跨层级,都使用 key 作为唯一标识来复用节点。最根本的区别在于它们的比较策略:React 使用单向遍历(从左到右),而 Vue 使用双端比较(从两端向中间)。这个差异导致了它们在处理节点移动时的不同性能表现。
另外,一个典型场景:
当最后一个节点被移动到列表开头时,React 会将前面的所有节点都向后移动,而 Vue 只会移动这一个节点。这表明 Vue 在处理节点位置变化时更高效。
还有一个重要区别是属性变化的处理:当节点元素类型相同但类名不同时,Vue 认为是不同类型元素会删除重建 ,而 React 认为是同类型节点只修改属性。
在优化策略上也有差异:Vue 3 引入了编译时优化(静态提升、Patch Flags),而 React 依赖 Fiber 架构和手动优化(如 shouldComponentUpdate。
更新机制也不同:Vue 在 diff 过程中同步更新真实 DOM ,而 React 先收集变更形成 effect list 再批量更新 。
对于列表处理,Vue 3 使用了最长递增子序列(LIS)算法来最小化节点移动 ,而 React 使用基于 index 和 lastIndex 的简单比较 。
旧节点abcd 新节点是 bcaf , vue 和 react 分别怎么diff的
Vue 的 Diff 过程(双端比较 + LIS)
-
初始化指针 :
旧: [a, b, c, d]
→ 头指针=0(a),尾指针=3(d)
新: [b, c, a, f]
→ 头指针=0(b),尾指针=3(f) -
双端比较:
- 头头对比:a ≠ b → ❌
- 尾尾对比:d ≠ f → ❌
- 头尾对比:a ≠ f → ❌
- 尾头对比:d ≠ b → ❌
-
非理想模式 (建立 key-index 映射):
新节点映射:
{b:0, c:1, a:2, f:3}
- 查找新头节点
b
:
旧索引=1 → 移动b
到旧头指针前(a 之前)
标记旧位置1为undefined
,新头指针右移 →新: [c, a, f]
- 查找新头节点
c
:
旧索引=2 → 移动c
到旧头指针前(a 之前)
标记旧位置2为undefined
,新头指针右移 →新: [a, f]
- 头头对比 :a = a → 复用,旧头指针右移 →
旧: [d]
,新头指针右移 →新: [f]
- 头头对比:d ≠ f → ❌
- 查找新头节点
f
:
旧索引不存在 → 创建 f 并插入旧头指针前(d 之前)
- 查找新头节点
-
清理旧节点 :
删除未处理的旧节点
d
-
最终操作:
js
移动节点: b, c (2次)
创建节点: f (1次)
删除节点: d (1次)
React 的 Diff 过程(单向遍历)
-
初始化映射 :
创建旧节点 key-index 映射:
{a:0, b:1, c:2, d:3}
初始化
lastIndex = 0
(记录当前已复用的最大旧索引) -
遍历新节点:
-
新节点
b
(key=b) :- 旧索引 = 1(
lastIndex=0
) - 1 > 0 → 复用旧节点 b,不移动
- 更新
lastIndex = max(0,1) = 1
- 旧索引 = 1(
-
新节点
c
(key=c) :- 旧索引 = 2(
lastIndex=1
) - 2 > 1 → 复用旧节点 c,不移动
- 更新
lastIndex = max(1,2) = 2
- 旧索引 = 2(
-
新节点
a
(key=a) :- 旧索引 = 0(
lastIndex=2
) - 0 < 2 → 移动节点 a 到当前位置(c 之后)
- 不更新
lastIndex
(仍为 2)
- 旧索引 = 0(
-
新节点
f
(key=f) :- 旧索引不存在 → 创建新节点 f
-
-
清理旧节点 :
删除未复用的旧节点
d
-
最终操作:
makefile
移动节点: a (1次)
创建节点: f (1次)
删除节点: d (1次)
💡 场景分析:
- React 优势 :当节点从头部移到尾部时(如 a 从开头移到末尾),只需移动 1 次。
- Vue 优势 :当节点整体轮转 时(如
[a,b,c,d]
→[d,a,b,c]
),Vue 只需移动 1 次(d),React 需移动 3 次。