陌上人如玉
公子世无双

12.9 React 与 Vue 的对比

12.9 React 与 Vue 的对比

一句话: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 要写依赖数组)
⑩ 忽略「团队熟悉度」这个最实际的因素
⑪ 忽略「生态可用性」(某个必要的库只有一方支持)
⑫ 把「响应式丢失」与「闭包陷阱」当成同一类问题(成因不同)

十二、面试延伸

  1. 「React 和 Vue 的核心区别是什么?」
    一句话:React 是「运行时 diff + 记忆化」,Vue 是「编译时优化 + 细粒度响应式」。具体表现为:① 数据模型——React 要求「不可变」(靠引用比较发现变化),Vue 用「Proxy 响应式」(可直接改,能精确知道哪个属性变了);② 更新粒度——React 的更新是「组件级」(父组件重渲染会让整棵未被 memo 拦住的子树重新执行),Vue 是「细粒度」(只有「依赖该数据的渲染副作用」会重新执行);③ 优化手段——React 主要靠「显式的 memo/useMemo/useCallback(以及新的 Compiler)」,Vue 靠「隐式的编译时分析与响应式追踪」。

  2. 「为什么 Vue 不需要 memo / useMemo?」
    因为 Vue 的更新模型不同:它基于「响应式依赖追踪」——组件的渲染副作用在运行时会收集自己依赖了哪些响应式数据,数据一变就只重新执行「依赖了它的那些渲染副作用」。所以:① 「跳过不需要更新的组件」是默认行为(不需要 memo);② 「缓存计算结果」用 computed(它自动追踪依赖,也不需要手写依赖数组)。而 React 的更新是「组件级」的,默认「重新执行组件函数、把子元素交给 diff」,所以「跳过」必须显式做。

  3. 「为什么 React 要求不可变数据,Vue 不要求?」
    因为 React 用 Object.is 比较引用来判断「状态是否变化」——如果直接改对象(user.name = 'b'),引用没变,React 就会认为「没变」而跳过更新(这也是「为什么必须写 setUser({...user, name: 'b'})」)。Vue 用 Proxy 拦截「属性读取与赋值」,能直接知道「哪个属性被改了」,所以「直接改也生效」。代价对比:React 的不可变带来「可预测、易做时间旅行」;Vue 的可变带来「写法自然」,但要小心「响应式丢失」(解构、传参时丢失代理)。

  4. 「React 的 Hooks 和 Vue 的组合式 API 有什么区别?」
    三点:① 执行次数——React 的组件函数(含 Hooks)每次渲染都重新执行,而 Vue 的 setup 只执行一次(后续更新靠响应式驱动);② 依赖声明——React 必须手写依赖数组(useEffect(fn, [dep]),漏了会有闭包陷阱),Vue 自动追踪(不需要声明);③ 顺序约束——React 的 Hooks 必须按固定顺序调用(不能在条件/循环里),Vue 的组合式 API 没有这个限制。这也解释了「为什么 React 里闭包陷阱很常见」——因为「每次渲染都是新的函数,捕获当次渲染的快照」。

  5. 「JSX 和模板哪个更好?」
    看「需要什么」:① 表达力——JSX 本质是 JS 表达式,可以写任意逻辑(「组件就是函数」),适合「复杂交互、动态结构」;模板受指令限制,需要靠 computed/方法补足;② 编译时优化——模板在编译时结构已知,能分析出「静态节点、动态绑定」(这是 Vue 能做 PatchFlags/Block Tree 的前提);JSX 是 JS,编译时难以确定结构;③ 学习曲线——模板更接近 HTML(直观),JSX 需要熟悉 JS 与「每次渲染都重新执行」的模型。两者可以互相借鉴(Vue 支持 JSX、React 也能用类模板的 DSL)。

  6. 「两个框架的 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,结构在运行时才确定)。

  7. 「各自的适用场景?」
    React 更适合:① 需要「极强表达力与灵活性」(复杂编辑器、可视化);② 团队熟悉函数式与 JS 生态;③ 需要 React Native 跨端;④ 需要 RSC/Server Components(Next.js);⑤ 依赖的库只有 React 版本。Vue 更适合:① 团队快速上手(模板直观、约定统一);② 中后台系统(Element Plus 等生态成熟);③ 希望「默认性能就够好」(不用手写 memo);④ 团队前端经验参差(约定强、风格统一)。但「团队熟悉度」往往比「技术优劣」更决定选型。

  8. 「从 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;选型看「表达力/跨端/生态」与「上手成本/约定强弱」,但「团队熟悉度」往往比技术优劣更关键;回答「哪个更好」时应该说「优化点不同、瓶颈常在框架之外」,而不是简单站队。

赞(0) 打赏
未经允许不得转载:陌上寒 » 12.9 React 与 Vue 的对比

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

微信扫一扫

支付宝扫一扫