React 和 Vue 都是用组件构建用户界面的 JavaScript 工具(React 称自己为库,Vue 称自己为渐进式框架),它们的大多数理念是相同的:props 向下传递,事件向上发送,响应式 state,虚拟 DOM。实际的区别在于:React 用 JSX 编写标记,并要求通过 setter 替换 state;而 Vue 在单文件组件中用 HTML 模板编写标记,并允许你直接修改响应式值。下面是 React 中的计数器:
同一个计数器写成 Vue 单文件组件(Counter.vue):
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<button @click="count++">Clicked {{ count }} times</button>
</template>
useState(0) 变成了 ref(0),onClick 变成了 @click,{count} 变成了 {{ count }}。最明显的区别是 count++:Vue 允许你直接修改值,它的响应式系统会察觉到。React 需要 setCount,因为只有当你交给它一个新值时它才会重新渲染。
JSX 与单文件组件
React 组件是一个返回 JSX 的 JavaScript 函数。条件用 && 或三元运算符,列表用 .map(),样式则由你自由选择(CSS 文件、CSS Modules、工具类框架)。
Vue 组件通常是一个包含三个部分的 .vue 文件:用于逻辑的 <script setup>、用于标记的 <template>,以及可选的 <style scoped>,其中的 CSS 只作用于该组件。模板使用指令:v-if、v-for、用于输入框双向绑定的 v-model、绑定值的 :prop 和监听事件的 @event。模板让懂 HTML 的人都能轻松读懂标记;JSX 则让你在标记中使用完整的 JavaScript。Vue 也能用 JSX,React 却不能用模板,但两边的大多数代码都遵循各自的默认写法。
下面是一个过滤列表,这是要对比的第二个功能。
试着输入 xyz,看看列表为空时的提示,然后删掉它。Vue 版本:
<script setup>
import { ref, computed } from 'vue';
const fruits = ['Apple', 'Banana', 'Cherry', 'Grape', 'Mango', 'Peach'];
const query = ref('');
const visible = computed(() =>
fruits.filter((f) => f.toLowerCase().includes(query.value.toLowerCase()))
);
</script>
<template>
<input v-model="query" placeholder="Filter fruit" />
<ul>
<li v-for="fruit in visible" :key="fruit">{{ fruit }}</li>
<li v-if="visible.length === 0">No match</li>
</ul>
</template>
v-model 取代了 value 加 onChange 的组合,computed 取代了普通变量。在 React 中,visible 每次渲染都会重新计算,因为组件函数会重新运行;在 Vue 中,computed 会缓存它,只在 query 变化时重新计算。两者都用 key 来区分列表项。
响应式模型
这是两者之间最根本的区别。
React:不可变 state 与重新渲染。state 是一个快照。你用新值调用 setter,React 再次运行组件函数,把新的 JSX 与旧的比较,然后更新 DOM。React 用 Object.is 比较值,所以原地修改一个对象或数组再把同一个对象传回去,看起来就像什么都没变。
点击“push() in place”,页面上什么都不会变,因为数组还是同一个对象。然后点击“New array”:渲染结果中也出现了 pushed,因为之前的修改确实发生了,只是 React 没有理由去查看。useState 页面深入讲解了这种模式。
Vue:可变的响应式值。ref 或 reactive 对象会记录谁读取了它(ref 通过它的 .value getter,reactive 对象通过 proxy)。当你写入它时,Vue 确切地知道哪些计算值、侦听器和模板部分依赖它,并且只更新这些部分。在 Vue 中,items.value.push('pushed') 就会更新页面。你需要写的样板代码更少,代价是失去了“值不会在你不知情时改变”的保证。
对于典型的应用来说,两种模型在速度上没有值得一提的差别。React 每次变化可能会运行更多的组件代码,并依靠记忆化(手写,或通过 React Compiler)来跳过不必要的工作;Vue 在运行时追踪依赖,所以默认就会跳过这些工作。
组合式 API 与 hooks
Vue 3 的组合式 API 和 React hooks 解决的是同一个问题:把相关的状态和逻辑放在一起,并在组件之间复用。Vue 把可复用的函数称为组合式函数(useMouse()),React 称之为自定义 hook(useMouse())。由于代码运行的时机不同,两者的规则也不同。
- 何时运行。Vue 的
setup在组件创建时运行一次。React 的组件函数每次渲染都会运行,其中的 hook 也随之运行。 - 顺序规则。React 按调用顺序匹配 hook,所以不能在条件语句或循环中调用它们。Vue 的
ref和computed没有这样的规则,因为 setup 只运行一次。 - 依赖。React 的 effect 和 memo 要在一个由你维护的数组中列出依赖。Vue 的
watchEffect和computed会自动追踪它们读取的值。 - 过期的值。React 的回调可能捕获之前某次渲染中的旧 state 值(经典的
setIntervalbug)。Vue 的回调在调用时读取count.value,总能看到当前的值。
React 的模型让每次渲染都是 props 和 state 的纯函数,便于测试和推理。Vue 的模型消除了依赖数组相关的一类 bug。掌握其中一个的开发者,通常很快就能上手另一个。
生态
| React | Vue | |
|---|---|---|
| 标记 | JSX | .vue 文件中的 HTML 模板(JSX 可选) |
| state 更新 | 用 setter 替换,然后重新渲染 | 修改响应式值,依赖它的部分随之更新 |
| 逻辑复用 | 自定义 hook | 组合式函数 |
| 派生值 | 在渲染时计算,useMemo | computed(带缓存) |
| 输入框双向绑定 | value 加 onChange | v-model |
| 作用域 CSS | 不内置(CSS Modules、第三方库) | 内置 <style scoped> |
| 路由 | React Router、TanStack Router、框架 | Vue Router(官方) |
| 全局状态 | context、Zustand、Redux Toolkit | Pinia(官方) |
| 全栈框架 | Next.js、React Router 框架模式 | Nuxt |
| 移动应用 | React Native | 无官方方案(Capacitor、NativeScript-Vue) |
| 构建工具 | Vite 或框架 | Vite |
| 维护者 | Meta 和社区 | 独立的核心团队和社区 |
这张表体现出的规律是:Vue 的核心团队同时维护路由、store 和开发者工具,所以 Vue 项目在每一方面都有明确的默认选择。React 把这些交给社区,给了你更多选项,也带来更多需要做的决定。Vite 是两者新项目常用的构建工具,它正是由 Vue 的作者发起的。
学习曲线
Vue 的入门通常更平缓。模板读起来就像你已经熟悉的 HTML,v-model 省去了受控输入框的样板代码,修改响应式值页面就会更新。React 一开始就要求你掌握 JSX 和不可变更新的习惯,大多数人首先需要看的是 JSX 页面。
掌握基础之后,差距会缩小。Vue 有自己的概念要学(ref 与 reactive 对象、脚本中要写 .value 而模板中不用、侦听器),React 则有 effect、记忆化和 hook 的使用规则。比起语法,你所在地区的招聘需求更适合用来做最终决定。
该选哪个
选择 React,如果:
- 你想要丰富的社区库和组件可选,或者打算用 React Native 开发原生移动应用。
- 你喜欢把逻辑和标记放在同一个 JavaScript 函数中,并想使用 Next.js 这样的框架。
- 你的团队看重明确、不可变的更新,让每次渲染都容易追踪。
选择 Vue,如果:
- 你想要官方配套的路由和状态管理工具,少做一些决定。
- 你的团队中有熟悉 HTML 模板的设计师或后端开发者。
- 你要给现有的服务端渲染页面加上交互:Vue 可以通过一个 script 标签直接放进页面,再逐步扩展。
两者都很成熟,被各种规模的公司用于生产环境,你在其中一个里学到的概念(组件、props、派生状态、带 key 的列表)可以直接用到另一个上。
常见问题
Vue 比 React 简单吗?
很多初学者一开始觉得 Vue 更简单,因为模板看起来就像普通的 HTML,修改响应式值(count.value++)不需要 setter 就能更新页面。React 要求你学习 JSX,并遵守 state 必须被替换而不是被修改的规则。过了最初几周,两者的难度就差不多了。
React 和 Vue 哪个更好?
总体上没有哪个胜出。React 把路由和状态管理交给社区库,并且有用于移动端的 React Native。Vue 的官方路由、store 和开发者工具由同一团队一起维护,很多人也觉得它的模板更容易阅读。选择你的团队或当地就业市场在用的那个。
Vue 的组合式 API 和 React hooks 有什么区别?
两者都把有状态的逻辑组织成可复用的函数。Vue 的 setup 每个组件只运行一次,并自动追踪依赖,所以没有依赖数组,也没有过期闭包问题。React 的组件函数每次渲染都会运行,所以 hook 每次都必须以相同的顺序调用,effect 也要列出自己的依赖。
Vue 使用 JSX 吗?
Vue 可以通过插件支持 JSX,但大多数 Vue 代码在单文件组件(.vue 文件)中使用 HTML 模板。React 几乎处处都用 JSX。
Vue 中对应 Next.js 的是什么?
Nuxt。就像 Next.js 之于 React,它在 Vue 之上加入了基于文件的路由、服务端渲染、静态生成和服务器路由。