面向 Vue3 开发者的 React 快速上手系列 · 开篇
作为一名 Vue3 开发者,你已经掌握了组件化、响应式、组合式 API 这些现代前端的核心心智模型。好消息是:React 解决的问题和 Vue 几乎完全一样,只是"哲学"不同。学 React 最快的方式不是从零背 API,而是把每个 React 概念对应到你熟悉的 Vue3 API 上。
这一篇不写代码细节,只帮你完成三件事:理解两者的根本差异、建立"翻译词典"、避开最容易踩的思维陷阱。
一、一句话概括两者的哲学差异
| Vue3 | React | |
|---|---|---|
| 核心信条 | 数据是什么(声明式响应式) | 如何根据数据渲染(函数式 UI) |
| 更新机制 | 细粒度追踪,谁依赖谁更新 | 状态变了,整个组件函数重新执行 |
| 心智模型 | 数据是"活的",你改 .value 它自己动 |
数据是"快照",你调用 setState 请求一次新渲染 |
| 语法载体 | 模板(Template)+ 指令 | JSX(JavaScript 里写 UI) |
最关键的一句话,请刻在脑子里:
Vue 是"响应式驱动"------你修改数据,视图自动跟着变。 React 是"重渲染驱动"------你请求一次新渲染,React 重新执行整个函数得到新 UI。
这个差异会解释后面 90% 的"为什么 React 要这样写"。
二、为什么 React 组件会"整个重新运行"?
在 Vue3 里,setup() 只运行一次,之后靠响应式系统精准更新:
js
// Vue3
const count = ref(0)
// setup 只执行一次,之后 count.value++ 会自动更新用到它的 DOM
function increment() {
count.value++
}
在 React 里,组件函数在每次渲染时都会从头到尾重新执行一遍:
jsx
// React
function Counter() {
const [count, setCount] = useState(0)
console.log('组件运行了') // 每次点击都会打印!
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
点一次按钮,Counter 函数就整个再跑一遍。这不是 bug,是 React 的设计核心。理解它,你就理解了:
- 为什么 React 需要
useState而不是普通变量(普通变量每次重跑都会被重置); - 为什么需要
useMemo/useCallback(避免每次重跑都重新计算/创建); - 为什么
useEffect需要"依赖数组"(告诉 React 什么时候该重新执行副作用)。
Vue 用响应式代理(Proxy)帮你自动追踪依赖;React 把这份"追踪"的工作,通过依赖数组交给了你手动声明。这是两者最大的体感差异,没有之一。
三、核心概念对照词典(收藏级)
这张表是整个系列的地图,后面每一篇都会展开其中一行。现在先扫一眼建立整体印象。
| 领域 | Vue3 | React 等效 | 关键差异 |
|---|---|---|---|
| 组件形式 | defineComponent / SFC |
函数组件 | React 只用函数组件,类组件已过时 |
| 局部状态 | ref() / reactive() |
useState() / useReducer() |
Vue 自动追踪;React 需 set 触发 |
| 派生值 | computed() |
useMemo() 或直接算 |
Vue 自动缓存;React 需声明依赖 |
| 侦听 | watch / watchEffect |
useEffect() |
Vue 自动收集依赖;React 手动写依赖数组 |
| 生命周期 | onMounted / onUnmounted |
useEffect + cleanup |
React 用一个 Hook 表达全部时机 |
| 模板语法 | <template> + 指令 |
JSX | React 在 JS 里写 UI |
| 条件渲染 | v-if / v-show |
&& / 三元表达式 |
React 用纯 JS 表达 |
| 列表渲染 | v-for + :key |
.map() + key |
同样需要 key |
| 双向绑定 | v-model |
value + onChange(受控组件) |
React 没有语法糖,需手写 |
| 组件传值 | props + defineProps |
函数参数 props |
React 更直白 |
| 子传父 | emit 事件 |
回调函数 props | React 把函数当 prop 传下去 |
| 插槽 | <slot> / 具名/作用域插槽 |
props.children / render props |
React 用 props 传 JSX |
| 依赖注入 | provide / inject |
createContext + useContext |
概念几乎一一对应 |
| 模板引用 | ref 属性 + .value |
useRef() + .current |
很相似 |
| 逻辑复用 | Composables(useXxx) |
自定义 Hook(useXxx) |
命名习惯都一样! |
| 路由 | Vue Router | React Router | 生态对应 |
| 全局状态 | Pinia | Zustand / Redux | 生态对应 |
| 工具库 | VueUse | ReactUse / ahooks | 甚至互相致敬 |
看到最后几行你应该松一口气了:Composables 和自定义 Hook 连命名规范(use 前缀)都一样。你的很多经验可以直接平移。
四、三个最容易踩的思维陷阱
陷阱 1:以为改了变量视图就会变
jsx
function Bad() {
let count = 0 // ❌ 普通变量
return <button onClick={() => { count++; console.log(count) }}>{count}</button>
}
count++ 确实变了,控制台也打印了新值,但界面纹丝不动 。因为 React 不知道你改了它,也不会重新渲染。必须用 useState,通过 setCount 来"请求渲染"。
陷阱 2:把 state 当成"立即生效"的可变数据
在 Vue,count.value++ 之后立刻读 count.value 就是新值。在 React,state 在一次渲染内是不可变的快照:
jsx
const [count, setCount] = useState(0)
function handle() {
setCount(count + 1)
console.log(count) // 仍然是旧值 0!新值要下次渲染才生效
}
陷阱 3:想直接"改" state 对象
Vue 的 reactive 对象可以随便改属性。React 的 state 必须创建新对象替换,不能原地修改:
jsx
// ❌ React 不认
user.name = 'Tom'; setUser(user)
// ✅ 创建新对象
setUser({ ...user, name: 'Tom' })
这条"不可变(immutability)"原则贯穿整个 React,务必记牢。
五、学习路线图
这个系列会沿着"你写一个 Vue 组件需要哪些能力"的顺序推进:
- ✅ 思维转变(本篇)
- JSX 与模板语法
- 组件与 Props
- 状态管理
useState/useReducer - 副作用与生命周期
useEffect - 派生状态与性能
useMemo/useCallback - 条件与列表渲染
- 事件处理与表单(受控组件)
- 组件通信(Context / 插槽等价物)
- 自定义 Hook(对标 Composables)
- 路由与状态管理生态
- 工程化 + 综合实战(用 Vite 从零写一个 Todo)
小结
- React 和 Vue 目标相同、哲学不同:Vue 响应式驱动,React 重渲染驱动。
- React 组件每次更新都会重新执行整个函数,这是理解一切的钥匙。
- 用"对照词典"平移你的 Vue 经验,比从零学快 3 倍。
- 牢记三条铁律:用
useState而非普通变量、state 是快照、state 不可变。
下一篇,我们从最直观的 JSX 开始,看看"在 JS 里写 UI"到底是怎么回事。