
一句话:React 是「运行时 diff + 手动/编译器记忆化 + 不可变数据」,Vue 是「编译时优化 + 细粒度响应式 + 可变数据」——两者不是「谁更快」,而是「不同的取舍」。
一、一句话总览
| 维度 | React | Vue 3 |
|---|---|---|
| 核心思想 | 运行时:UI = f(state),靠 diff 找变化 | 编译时 + 响应式:精确知道「谁依赖谁」 |
| 数据模型 | 不可变(每次产生新对象) | 可变(Proxy 直接改) |
| 更新粒度 | 组件级(整棵子树重新执行,除非 memo) | 细粒度(精确到「依赖该数据的节点」) |
| 视图描述 | JSX(JS 的完整表达力) | 模板(编译时可分析) |
| 优化手段 | memo / useMemo(手动)+ Compiler(自动) |
编译时静态分析(自动,无需手写) |
| 逻辑复用 | Hooks(函数,每次渲染重新执行) | 组合式 API(setup 只执行一次) |
| 学习曲线 | 更「JS 化」(要理解闭包/引用/渲染模型) | 更「框架化」(模板语法 + 响应式心智) |
| 生态 | 最庞大(Next.js、RN、无数库) | 完整但相对小(Nuxt、Vite 同源) |
| 团队协作 | 自由度高(需要强规范) | 约定强(官方风格指南) |
⭐ 最关键的一句话区分:
· React:「我不知道哪些数据变了,所以我重新执行组件,再用 diff 比较」
· Vue: 「我知道哪些数据变了(响应式依赖追踪),我直接更新对应的节点」
二、响应式原理对比
// React:不可变 —— 「改数据」= 「产生新对象」
function ReactCounter() {
const [user, setUser] = useState({ name: 'a', age: 1 });
const updateName = () => {
setUser((prev) => ({ ...prev, name: 'b' })); // ⭐ 创建新对象(引用变化)
};
// React 靠「引用变化」发现「数据变了」(Object.is 比较)
}
// Vue:可变 —— 直接改,Proxy 拦截并通知
const user = reactive({ name: 'a', age: 1 });
const updateName = () => {
user.name = 'b'; // ⭐ 直接改(Proxy 拦截)
};
// Vue 靠「Proxy 的 get 收集依赖 / set 触发通知」
| 维度 | React(不可变) | Vue(Proxy 响应式) |
|---|---|---|
| 改数据 | setX({ ...prev, k: v }) |
obj.k = v |
| 如何发现变化 | 引用比较(Object.is) |
Proxy 拦截 |
| 直接改对象 | ❌ 不生效(引用没变) | ✅ 生效 |
| 心智负担 | ⚠️ 要记住「不可变」 | ✅ 像普通对象 |
| 调试 | 每次变化都是新对象(易做撤销/时间旅行) | 需要追踪 Proxy |
⭐ React 的「不可变」带来的两端:
✅ 好处:变化可预测、易于「时间旅行/撤销重做」、与 memo 的浅比较天然配合
❌ 代价:深嵌套更新要「逐层展开」(需要 Immer」,且必须记住「不能直接改」
⭐ Vue 的「Proxy 响应式」带来的两端:
✅ 好处:写法自然(直接改)、不需要「展开」、依赖追踪自动
❌ 代价:需要理解「响应式失去」(解构/传参丢失响应性)、调试 Proxy 略麻烦
三、更新粒度对比(最本质的差异)
// React:父组件重渲染 → 子组件默认也重新执行(除非 memo)
function Parent() {
const [count, setCount] = useState(0);
console.log('Parent 渲染');
return (
<>
<button onClick={() => setCount((c) => c + 1)}>{count}</button>
<Child /> {/* ⚠️ Child 函数会被重新调用 */}
</>
);
}
function Child() {
console.log('Child 渲染'); // ⚠️ 会打印
return <div>我没有 props</div>;
}
<!-- Vue:只有「依赖了 count 的节点」会更新 -->
<template>
<div>
<button @click="count++">{{ count }}</button>
<Child />
</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<!-- Child.vue -->
<template>
<div>我没有 props</div>
</template>
<!-- ⭐ Child 不会重新渲染(因为它不依赖 count) -->
| 维度 | React | Vue |
|---|---|---|
| 重渲染单位 | 组件(函数重新执行) | 响应式副作用(精确到节点) |
| 父组件更新 | 子组件默认也重新执行 | 子组件不重新渲染(除非它依赖了数据) |
| 需要手动优化 | ✅(memo/useMemo/useCallback) |
⚠️ 很少(编译时自动) |
| 心智能否「不管性能就写」 | ⚠️ 大列表需要优化 | ✅ 默认够用(但也有极限) |
⭐ 这解释了「为什么 Vue 不需要 memo」:
因为 Vue 的更新基于「数据 → 依赖该数据的副作用」,
而不是「组件 → 子组件」的传播。
它「精确地知道」哪些渲染函数需要重跑。
⭐ 也解释了「为什么 React 需要 memo 与 React Compiler」:
因为 React 的更新是「组件级」的,
默认「重新执行组件函数并把子元素交给 diff」——
「跳过不需要的」这件事必须显式做(手动或编译器)。
四、模板 vs JSX
// JSX:本质是 JS 的表达式(完整表达力)
function List({ items, showEmpty }) {
return (
<ul>
{items.length > 0
? items.map((it) => <li key={it.id}>{it.name}</li>)
: showEmpty && <li>暂无数据</li>}
</ul>
);
}
<!-- 模板:编译时可分析(但表达力受限于指令) -->
<template>
<ul>
<li v-for="it in items" :key="it.id">{{ it.name }}</li>
<li v-if="items.length === 0 && showEmpty">暂无数据</li>
</ul>
</template>
| 维度 | JSX | 模板 |
|---|---|---|
| 本质 | JS 表达式(可以写任意逻辑) | HTML + 指令(编译成渲染函数) |
| 表达力 | ✅ 极高(需要时能写复杂逻辑) | ⚠️ 受指令限制(复杂逻辑要写 computed/方法) |
| 编译时优化 | ⚠️ 难(结构在运行时才确定) | ✅ 容易(静态分析出「动态节点」) |
| 语法错误检测 | 需要 lint | ✅ 模板编译器能报更多错 |
| 学习成本 | 需要熟悉 JS 与「每次渲染都重新执行」 | 模板语法更直观 |
| 与 HTML 相似度 | 中(属性名要驼峰) | ✅ 高(保留 v-* 指令) |
⭐ 一句话权衡:
· JSX:表达力更强(「组件就是函数」),但「编译时难以分析」
· 模板:编译时更容易优化,但表达力需要靠「指令 + computed」补足
⚠️ 这也是「Vue 能做大编译时优化」的原因——
模板结构在编译时就确定了,能分析出「哪些节点是静态的」。
五、diff 对比
| 维度 | React | Vue 3 |
|---|---|---|
| 同层比较 | ✅ | ✅ |
| 移动判定 | lastPlacedIndex 单指针贪心 |
双端比较 + LIS(更接近最少) |
| 编译时优化 | 较少(Compiler 在补) | 大量:PatchFlags、Block Tree、静态提升 |
| 静态节点处理 | 靠 memo / 元素引用稳定 |
编译时提升(hoisted,只创建一次) |
| 动态绑定信息 | 运行时才知道 | 编译时标记(PatchFlags 告诉运行时「只有 class 变了」) |
⭐ Vue 3 的 Block Tree 是什么?
编译时把「模板」切成「Block」(动态节点的集合):
· 静态节点「提升」到渲染函数外(只创建一次)
· 每个 Block 记录「动态后代」的列表(`dynamicChildren`)
→ diff 时「只遍历 dynamicChildren」而不是「遍历整棵树」
→ 这是 Vue 3「跳过大量比较」的关键
⚠️ React 的对应手段是「运行时 memo + Compiler」:
它无法在编译时「先天地」标记动态节点(因为 JSX 是 JS),
所以要用「记忆化」的方式告诉运行时「这块不用重算」。
六、优化手段对比
| 优化点 | React | Vue |
|---|---|---|
| 跳过子组件渲染 | React.memo(+ 稳定 props) |
默认就跳过(除非它依赖了数据) |
| 缓存计算结果 | useMemo |
computed(自动追踪依赖) |
| 稳定函数引用 | useCallback |
❌ 不需要(函数在 setup 里创建一次) |
| 大列表 | 虚拟滚动(react-window 等) |
虚拟滚动(vue-virtual-scroller 等) |
| 静态内容 | memo / 元素引用稳定 |
编译时静态提升 |
| 自动优化 | React Compiler(编译时插记忆化) | ✅ 默认(编译时分析) |
⭐ 一个关键差异:
· React 的「优化」大部分是「显式」的(memo/useMemo/useCallback),
所以「不优化就容易慢」,也因此需要「理解渲染机制」。
· Vue 的「优化」大部分是「隐式」的(编译时 + 响应式),
所以「默认就够快」,代价是「黑盒感更强」(不容易看出「为什么没更新」)。
七、逻辑复用对比
// React:Hooks(函数,每次渲染都会重新执行)
function useCounter(initial = 0) {
const [count, setCount] = useState(initial); // ⭐ 每次渲染都执行
const increment = useCallback(() => setCount((c) => c + 1), []);
return { count, increment };
}
// Vue:组合式 API(setup 只执行一次)
export function useCounter(initial = 0) {
const count = ref(initial); // ⭐ 只在 setup 时执行一次
const increment = () => count.value++;
return { count, increment };
}
| 维度 | React Hooks | Vue 组合式 API |
|---|---|---|
| 执行次数 | 每次渲染都执行 | 只执行一次(setup 阶段) |
| 闭包问题 | ⚠️ 常见(stale closure,见 2.13) | ⚠️ 较少(ref 是「可变的容器」) |
| 依赖数组 | ✅ 必须维护 | ❌ 不需要(响应式自动追踪) |
| 顺序约束 | ✅ 必须稳定(不能在条件里) | ❌ 没有这个约束 |
| 值的读取 | 直接读(当次渲染的快照) | .value(响应式容器) |
⭐ 这解释了两个「显著的体感差异」:
① React 里「闭包陷阱」很常见(因为每次渲染都是新函数、捕获新快照),
而 Vue 里「同一个 ref 对象始终是同一个」(所以拿到的永远是最新值)。
② React 里「依赖数组」是必须维护的负担,
而 Vue 靠「响应式自动追踪依赖」省掉了这一环。
八、生态与工程
React 生态:
· 框架:Next.js(全栈)、Remix / React Router(数据路由)
· 移动端:React Native
· 状态:Zustand / Redux / Jotai / TanStack Query
· UI:MUI / Ant Design / shadcn/ui / Radix
· 优点:生态最大、跨端最成熟(RN)
· 缺点:选择太多(每个问题有 5 个方案)、需要自己定规范
Vue 生态:
· 框架:Nuxt(全栈)
· 状态:Pinia(官方)
· UI:Element Plus / Vuetify / Naive UI
· 优点:官方风格统一(官方路由/状态/构建工具)、上手快
· 缺点:跨端(Vue Native 不活跃)、大公司采用相对少
⭐ 工程上的现实差异:
· React:自由度高 → 「团队规范」极其重要(否则 10 个人 10 种写法)
· Vue:约定强 → 上手快、风格统一,但「深度定制」时可能觉得受限
九、各自适合的场景
✅ 更适合 React:
① 需要「极强的表达力与灵活性」(复杂交互、可视化编辑器)
② 团队熟悉「函数式编程」与「JS 生态」
③ 需要 React Native 做跨端
④ 需要 RSC / Server Components(Next.js)
⑤ 生态依赖多(某个库只有 React 版本)
✅ 更适合 Vue:
① 团队快速上手(模板语法直观、约定统一)
② 中小型项目 / 后台管理系统(Element Plus 生态成熟)
③ 希望「默认性能就够好」(不用手写 memo)
④ 团队前端经验参差(约定强、代码风格统一)
⭐ 但「团队熟悉度」往往比「技术优劣」更决定选型——
两个框架都能做出高性能的大型应用,
真正的风险是「用不熟的那一个」。
十、常见追问(怎么回答「哪个更好」)
Q: React 和 Vue 哪个更快?
A: 「不能一概而论——它们优化点不同:
React 的收益在「组件级记忆化 + React Compiler」,
Vue 的收益在「编译时静态分析 + 细粒度响应式」。
在「模板场景、静态内容多」的页面上 Vue 常有更少的运行时开销;
在「复杂逻辑、需要 JS 表达力」的场景 React 更灵活。
而且性能瓶颈往往不在框架本身(在数据量、网络、DOM 数量)。」
Q: 为什么 Vue 不需要 useMemo?为什么 React 不用?
A: 「因为更新模型不同:
Vue 靠『响应式依赖追踪』——它知道「哪个渲染副作用依赖了哪些数据」,
数据变了自己触发对应的更新;所以「计算缓存」用 computed 就够。
React 靠『组件级重新执行 + diff』——它不知道「哪个节点依赖了哪个数据」,
所以要靠 memo/useMemo 在「引用」层面告诉它『这块不用重算』。」
Q: 为什么 React 需要「不可变数据」,Vue 不需要?
A: 「因为 React 用『引用比较』判断状态是否变化——
直接改对象引用不变 → React 认为没变 → 不更新。
Vue 用 Proxy 拦截属性访问与赋值,能直接知道「哪个属性变了」,
所以直接改也生效。」
Q: Vue 的响应式有什么坑?
A: 「响应式丢失——解构、传参、展开时会把『ref/reactive 的代理』丢掉,
需要用 toRefs / toRef 保持响应性;
以及『直接替换整个 reactive 对象』会断开代理。
React 侧的对应问题是『闭包陷阱』(读到旧快照),
两者都是『数据与视图绑定机制』带来的心智负担。」
Q: 两个框架的 diff 有什么区别?
A: 「React 是『运行时 diff 整棵树(同层)+ 单指针贪心移动判定』;
Vue 3 是『编译时生成 Block Tree + PatchFlags(标记动态节点)+
双端比较 + LIS(最少移动)』。
Vue 的 diff 更『有信息』(编译时知道哪里动态),
React 则更多依赖运行时记忆化。」
Q: 现在学哪个更有前途?
A: 「都值得学——但『React 的岗位更多、生态更大』是事实;
Vue 在国内中后台市场占有率高。
从『理解现代前端』的角度,两个都学:它们代表了
『运行时 diff + 记忆化』与『编译时优化 + 响应式』两条技术路线,
理解差异比记住 API 更有价值。」
Q: 如果从 Vue 转 React,最难理解的是什么?
A: 「三件事:①『每次渲染都重新执行组件函数』(所以有闭包陷阱);
②『不可变更新』(不能直接改对象);
③『更新是组件级传播的』(所以要理解 memo 与引用稳定)。
反之从 React 转 Vue,最容易困惑的是『响应式为什么有时会丢』。」
Q: JSX 和模板哪个更好?
A: 「看需求:JSX 表达力强(组件就是函数,能写任意 JS),
模板编译时更容易优化、语法更接近 HTML。
Vue 也有 JSX 支持,React 也可以用模板 DSL(少)。
本质区别是『编译时能拿到多少结构信息』。」
十一、常见坑
① 比较时只看「跑分」(真实项目瓶颈常在数据量/网络/DOM)
② 把「Vue 不需要手动优化」理解成「Vue 没有性能问题」
(大列表、复杂组件同样需要优化)
③ 把「React 要写 memo」理解成「React 难用」
(有 React Compiler 后大幅改善,且理解机制是关键)
④ 从 Vue 转 React 时「直接改 state 对象」(不生效)
⑤ 从 React 转 Vue 时「解构 reactive 对象」(响应式丢失)
⑥ 认为「Vue 的模板不能写复杂逻辑」(可以用 computed/方法/JSX)
⑦ 认为「React 的 Hooks 就是 Vue 的 setup 的等价物」
(执行次数不同:每次渲染 vs 只执行一次)
⑧ 用 React 的思维写 Vue(如「到处 useMemo 等价物」→ 其实用 computed)
⑨ 用 Vue 的思维写 React(如「依赖 ref 自动追踪」→ React 要写依赖数组)
⑩ 忽略「团队熟悉度」这个最实际的因素
⑪ 忽略「生态可用性」(某个必要的库只有一方支持)
⑫ 把「响应式丢失」与「闭包陷阱」当成同一类问题(成因不同)
十二、面试延伸
- 「React 和 Vue 的核心区别是什么?」
一句话:React 是「运行时 diff + 记忆化」,Vue 是「编译时优化 + 细粒度响应式」。具体表现为:① 数据模型——React 要求「不可变」(靠引用比较发现变化),Vue 用「Proxy 响应式」(可直接改,能精确知道哪个属性变了);② 更新粒度——React 的更新是「组件级」(父组件重渲染会让整棵未被 memo 拦住的子树重新执行),Vue 是「细粒度」(只有「依赖该数据的渲染副作用」会重新执行);③ 优化手段——React 主要靠「显式的memo/useMemo/useCallback(以及新的 Compiler)」,Vue 靠「隐式的编译时分析与响应式追踪」。 -
「为什么 Vue 不需要
memo/useMemo?」
因为 Vue 的更新模型不同:它基于「响应式依赖追踪」——组件的渲染副作用在运行时会收集自己依赖了哪些响应式数据,数据一变就只重新执行「依赖了它的那些渲染副作用」。所以:① 「跳过不需要更新的组件」是默认行为(不需要memo);② 「缓存计算结果」用computed(它自动追踪依赖,也不需要手写依赖数组)。而 React 的更新是「组件级」的,默认「重新执行组件函数、把子元素交给 diff」,所以「跳过」必须显式做。 -
「为什么 React 要求不可变数据,Vue 不要求?」
因为 React 用Object.is比较引用来判断「状态是否变化」——如果直接改对象(user.name = 'b'),引用没变,React 就会认为「没变」而跳过更新(这也是「为什么必须写setUser({...user, name: 'b'})」)。Vue 用 Proxy 拦截「属性读取与赋值」,能直接知道「哪个属性被改了」,所以「直接改也生效」。代价对比:React 的不可变带来「可预测、易做时间旅行」;Vue 的可变带来「写法自然」,但要小心「响应式丢失」(解构、传参时丢失代理)。 -
「React 的 Hooks 和 Vue 的组合式 API 有什么区别?」
三点:① 执行次数——React 的组件函数(含 Hooks)每次渲染都重新执行,而 Vue 的setup只执行一次(后续更新靠响应式驱动);② 依赖声明——React 必须手写依赖数组(useEffect(fn, [dep]),漏了会有闭包陷阱),Vue 自动追踪(不需要声明);③ 顺序约束——React 的 Hooks 必须按固定顺序调用(不能在条件/循环里),Vue 的组合式 API 没有这个限制。这也解释了「为什么 React 里闭包陷阱很常见」——因为「每次渲染都是新的函数,捕获当次渲染的快照」。 -
「JSX 和模板哪个更好?」
看「需要什么」:① 表达力——JSX 本质是 JS 表达式,可以写任意逻辑(「组件就是函数」),适合「复杂交互、动态结构」;模板受指令限制,需要靠computed/方法补足;② 编译时优化——模板在编译时结构已知,能分析出「静态节点、动态绑定」(这是 Vue 能做PatchFlags/Block Tree 的前提);JSX 是 JS,编译时难以确定结构;③ 学习曲线——模板更接近 HTML(直观),JSX 需要熟悉 JS 与「每次渲染都重新执行」的模型。两者可以互相借鉴(Vue 支持 JSX、React 也能用类模板的 DSL)。 -
「两个框架的 diff 有什么区别?」
① 移动判定——React 用lastPlacedIndex单指针贪心(不保证最少移动),Vue 3 用「双端比较 + 最长递增子序列(LIS)」(接近最少移动);② 编译时优化——Vue 3 有PatchFlags(标记「这个节点只有 class 变了」) 与 Block Tree(dynamicChildren,diff 时只遍历动态后代)、静态提升;React 主要靠运行时记忆化(memo/useMemo)与 React Compiler;③ 本质上——Vue 的 diff「更有信息」(编译时就知道哪里动态),React 的 diff「更通用」(因为 JSX 是 JS,结构在运行时才确定)。 -
「各自的适用场景?」
React 更适合:① 需要「极强表达力与灵活性」(复杂编辑器、可视化);② 团队熟悉函数式与 JS 生态;③ 需要 React Native 跨端;④ 需要 RSC/Server Components(Next.js);⑤ 依赖的库只有 React 版本。Vue 更适合:① 团队快速上手(模板直观、约定统一);② 中后台系统(Element Plus 等生态成熟);③ 希望「默认性能就够好」(不用手写 memo);④ 团队前端经验参差(约定强、风格统一)。但「团队熟悉度」往往比「技术优劣」更决定选型。 -
「从 Vue 转 React 最难理解的是什么?」
三件事:① 「每次渲染都重新执行组件函数」——这意味着「函数里的局部变量每次都是新的」「闭包会捕获当次快照」(由此产生「闭包陷阱」与「必须用函数式更新」);② 「不可变更新」——不能直接改state对象,必须「产生新对象」(否则 React 发现不了变化);③ 「更新是组件级传播的」——父组件重渲染会让子组件也重新执行,所以要理解「memo+ 引用稳定」这套优化体系。反过来从 React 转 Vue,最容易困惑的是「响应式为什么有时会丢」(解构、传参、替换整个对象时)。
一句话速记
React = 「运行时 diff(同层)+ 不可变数据 + 组件级更新 + 显式记忆化(
memo/useMemo,以及 Compiler)」,Vue = 「编译时优化(PatchFlags/Block Tree/静态提升)+ Proxy 响应式(可直接改)+ 细粒度更新 + 隐式优化(computed自动追踪)」;React 靠引用比较发现变化(所以必须不可变)、Vue 靠 Proxy 拦截(所以可直接改,但要小心响应式丢失);React 的 Hooks 每次渲染都执行 + 必须手写依赖 + 必须顺序稳定,Vue 的setup只执行一次 + 依赖自动追踪 + 无顺序约束;diff 上 React 是单指针贪心、Vue 是双端 + LIS;选型看「表达力/跨端/生态」与「上手成本/约定强弱」,但「团队熟悉度」往往比技术优劣更关键;回答「哪个更好」时应该说「优化点不同、瓶颈常在框架之外」,而不是简单站队。



最新评论
读过书不知道欧·亨利的人少。教科书上选文有
这小生活不错呀
不错,必须顶一下!
看着你还在坚持,很好
看来忙了也没时间更新博客了
NIce。学习了。。。。
网站不错!!!!
简洁实用,好文章!