上篇回顾:2.2-01 拆解了 Vue3 的 Proxy 响应式、组合式 API、组件通信、双向绑定本质。本篇讲 React——前端另一大框架。对后端工程师来说,React 的心智模型比 Vue 陡峭得多(JSX + Hooks + 不可变数据),但理解 React 能让你形成「为什么同样的需求两种框架设计完全不同」的判断力。
一、开篇:同一个需求,Vue 和 React 两种写法
需求:一个计数器,点击 +1,显示当前值。
Vue3:
<template> <button @click="count++">{{ count }}</button> </template> <script setup> import { ref } from 'vue'; const count = ref(0); </script>React:
import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>{count}</button>; }表面看差不多,但底层哲学截然不同:
- Vue:
count++直接修改数据,响应式自动更新 UI - React:
setCount(count + 1)创建新值触发重新渲染,不能直接修改 count
这就是本篇的核心命题:数据可变 vs 数据不可变,两种哲学各自把复杂度推给了谁。
二、JSX:HTML in JavaScript
2.1 JSX 是什么
JSX 不是 HTML,是 JavaScript 的语法扩展:
const element = <h1 className="title">Hello</h1>;编译后变成:
constelement=React.createElement('h1',{className:'title'},'Hello');所以 JSX 本质是createElement的语法糖。
2.2 与 Vue 模板的区别
| 维度 | Vue 模板 | JSX |
|---|---|---|
| 语言 | HTML 扩展(v-if/v-for) | JavaScript 表达式 |
| 灵活度 | 受限(模板语法) | 完全灵活(JS 能力) |
| 学习曲线 | 低(像 HTML) | 中(要懂 JS) |
| 类型检查 | 弱 | 强(TS 友好) |
Vue 模板把逻辑限制在模板语法内,JSX 把模板融入 JS。各有优劣——Vue 对新手友好,JSX 对高级用户灵活。
三、虚拟 DOM 与 diff 策略
3.1 为什么需要虚拟 DOM
直接操作真实 DOM 很慢(重排重绘)。虚拟 DOM 是一棵 JS 对象树,修改时先改虚拟 DOM,再 diff 出最小变化,批量更新真实 DOM。
// 虚拟 DOM 节点{type:'div',props:{className:'box'},children:[{type:'span',props:{},children:['Hello']}]}3.2 diff 算法的三个假设
- 不同类型节点 → 直接替换(div 变 span,整个子树重建)
- 同类型节点 → 复用 DOM,更新属性
- 列表 diff → 用 key 标识(key 相同复用,不同销毁重建)
3.3 key 的重要性
// 没 key:React 按位置 diff,可能错乱 items.map(item => <li>{item.text}</li>); // 有 key:React 按 key diff,精准匹配 items.map(item => <li key={item.id}>{item.text}</li>);不要用 index 做 key——列表中间插入/删除时,index 变化导致 React 误判,性能差且可能 bug。
3.4 Vue 和 React 的虚拟 DOM 一样吗
底层思路一样(diff + 最小更新),但:
- Vue2:全量 diff
- Vue3:编译时标记静态节点,diff 时跳过(PatchFlag)
- React:Fiber 架构,可中断 diff(时间分片)
四、Hooks:函数式组件的状态
4.1 React 的函数式理念
React 推崇函数式编程:组件是纯函数,输入 props 输出 UI。但纯函数没有状态——Hooks 就是给函数组件加状态的「钩子」。
function Counter() { const [count, setCount] = useState(0); // 状态钩子 return <button onClick={() => setCount(count + 1)}>{count}</button>; }4.2 useState 的正确理解
const[count,setCount]=useState(0);count:当前值(不可变!不能count++)setCount:触发更新的函数0:初始值
setCount做两件事:
- 更新内部状态(创建新值,不修改旧的)
- 触发组件重新渲染
4.3 useEffect:副作用钩子
function UserList() { const [users, setUsers] = useState([]); useEffect(() => { fetch('/api/users') .then(res => res.json()) .then(data => setUsers(data)); }, []); // 空依赖数组:只在挂载时执行一次 return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>; }useEffect的依赖数组:
- 不传:每次渲染后都执行
[]:只在挂载时执行[dep1, dep2]:dep 变化时执行
4.4 依赖数组的陷阱
useEffect(() => { fetch(`/api/user/${userId}`) .then(setUser); }, [userId]);如果忘了加userId到依赖数组,userId 变了但 effect 不重新执行——数据不更新。这是 React 最常见的 bug 之一。
ESLint 的react-hooks/exhaustive-deps规则会自动检测这个问题。
五、数据不可变:React 的核心原则
5.1 为什么不能直接修改
// 错误:直接修改 state const [list, setList] = useState([1, 2, 3]); list.push(4); setList(list); // React 不会重新渲染!React 用Object.is比较新旧 state,引用相同就跳过渲染。list.push没改引用,React 认为没变。
// 正确:创建新数组 setList([...list, 4]); // 新引用,触发渲染5.2 不可变数据的哲学
React 的核心理念:数据不可变(immutable)。每次修改都创建新值,旧值不变。
好处:
- 时间旅行(undo/redo):旧状态还在
- 性能优化:引用比较比深比较快
- 并发安全:不可变数据天然线程安全
代价:
- 内存开销:每次创建新对象
- 心智负担:要记住不能直接改
5.3 Vue 的可变哲学
conststate=reactive({list:[1,2,3]});state.list.push(4);// 直接修改,响应式触发Vue 用 Proxy 拦截修改,知道你改了什么。不需要创建新对象。
5.4 两种哲学把复杂度推给谁
| 哲学 | 复杂度推给 | 优势 |
|---|---|---|
| React 不可变 | 开发者(要手动创建新值) | 性能优化简单(引用比较) |
| Vue 可变 | 框架(Proxy 拦截要追踪所有修改) | 开发者心智负担小 |
结论:React 把数据管理的复杂度推给开发者,换性能优化的简单;Vue 把复杂度吞进框架,换开发体验的简洁。没有对错,看团队偏好。
六、受控组件 vs 非受控组件
6.1 受控组件
function Form() { const [text, setText] = useState(''); return <input value={text} onChange={e => setText(e.target.value)} />; }输入框的值由 React state 控制。每次输入都触发 setState → 重新渲染。
6.2 非受控组件
function Form() { const inputRef = useRef(); return <input ref={inputRef} defaultValue="" />; }输入框的值由 DOM 自己管,React 通过 ref 读取。
推荐:表单用受控组件(数据在 React 里,便于校验和提交);简单场景用非受控(性能好)。
七、Vue vs React 选型对比
| 维度 | Vue3 | React |
|---|---|---|
| 心智模型 | 可变数据 + 响应式 | 不可变数据 + 重新渲染 |
| 模板 | 模板语法 | JSX |
| 状态 | ref/reactive | useState/useReducer |
| 副作用 | watch/watchEffect | useEffect |
| 生态 | 国内中后台强 | 国际化、跨平台 |
| TS 支持 | 好(Vue3 重写) | 极好(JSX 天然适配) |
| 学习曲线 | 平缓 | 陡峭 |
| 适合场景 | 管理后台、中后台 | 复杂交互、React Native |
后端转前端推荐 Vue3,但理解 React 能让你明白「为什么有不同的设计选择」。
八、小结表
| 概念 | React | Vue 对照 |
|---|---|---|
| 视图描述 | JSX | 模板 |
| 状态 | useState | ref/reactive |
| 数据原则 | 不可变 | 可变 |
| 副作用 | useEffect | watchEffect |
| 性能优化 | 引用比较 | Proxy 追踪 |
| 列表 key | 必须 | 必须 |