这仍然是一篇AI文章,足够幸运地生活在这个AI时代,知识的获取从未变得如此容易。
作者:Qwen3-Max
目标读者:熟悉 Vue 2/3 的前端开发者,希望系统掌握 React 核心概念与开发范式
目录
- 一、React 与 Vue 的核心哲学差异
- 二、项目初始化与开发环境搭建
- 三、组件基础:函数组件与 JSX
- 四、状态管理:useState 与不可变性原则
- 五、副作用处理:useEffect 全面解析
- 六、派生状态与性能优化:useMemo 与 useCallback
- 七、组件通信模式详解
- 八、Context API 与全局状态管理
- 九、现代状态管理方案:Zustand 实战
- 十、路由系统:React Router v6 指南
- 十一、表单处理:受控组件与自定义 Hook
- 十二、常见陷阱与最佳实践
- 十三、进阶建议与学习路径
一、React 与 Vue 的核心哲学差异
| 维度 | Vue | React |
|---|---|---|
| 响应式机制 | 自动依赖追踪(Proxy / defineProperty) | 手动触发重渲染(通过状态 setter) |
| UI 更新模型 | 数据变化 → 自动更新 DOM | 状态变化 → 重新执行组件函数 → Virtual DOM diff → 更新 DOM |
| 模板语言 | <template> + 指令(v-if, v-for) | JSX:JavaScript 的语法扩展 |
| 组件组织 | Options API(data/methods/computed)或 Composition API | 纯函数 + Hooks(逻辑复用靠自定义 Hook) |
| 数据流 | 支持 v-model 双向绑定语法糖 | 严格单向数据流,双向绑定需手动组合 value + onChange |
💡 关键思维转变:
- Vue 是 “数据驱动视图”:你改数据,框架自动更新 UI。
- React 是 “函数驱动 UI”:UI = f(state),状态变 → 函数重执行 → 新 UI。
二、项目初始化与开发环境搭建
推荐使用 Vite(极速、现代化)
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev
生成的目录结构:
my-react-app/
├── public/
│ └── favicon.ico
├── src/
│ ├── assets/
│ ├── components/
│ ├── App.jsx
│ └── main.jsx
├── index.html
└── package.json
关键文件说明
main.jsx:应用入口,类似 Vue 的main.jsApp.jsx:根组件,类似App.vue- 所有组件默认使用
.jsx后缀(也可用.tsx支持 TypeScript)
✅ 建议立即启用 TypeScript:
npm create vite@latest my-app -- --template react-ts
三、组件基础:函数组件与 JSX
1. 函数组件写法
// Greeting.jsx
export default function Greeting({ name }) {
return <h1>Hello, {name}!</h1>
}
⚠️ 注意:
- 组件名必须大写(
Greeting✅,greeting❌)- props 是函数参数(对象解构)
2. JSX 语法规则
| Vue | React (JSX) |
|---|---|
<div class="btn"> | <div className="btn"> |
<label for="id"> | <label htmlFor="id"> |
<input v-model="text"> | <input value={text} onChange={e => setText(e.target.value)} /> |
{{ message }} | {message} |
v-html="raw" | <div dangerouslySetInnerHTML={{ __html: raw }} /> |
3. 条件渲染
// v-if 对应
{isVisible && <Modal />}
{isLoading ? <Spinner /> : <Content />}
// 不支持 v-show(需手动控制 style.display)
<div style={{ display: show ? 'block' : 'none' }}>...</div>
4. 列表渲染
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li> // ⚠️ 必须有 key!
))}
</ul>
🔑
key的作用:帮助 React 识别哪些元素被添加/删除/移动,提升 diff 效率。
四、状态管理:useState 与不可变性原则
1. useState 基础
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0) // 初始值为 0
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
2. 不可变性(Immutability)是核心!
❌ 错误做法(直接修改原对象/数组):
const [user, setUser] = useState({ name: 'Alice' })
user.name = 'Bob' // ❌ 直接修改!React 无法检测变化
setUser(user)
✅ 正确做法(返回新对象):
setUser({ ...user, name: 'Bob' }) // ✅ 展开新对象
// 数组示例
setList([...list, newItem]) // 添加
setList(list.filter(item => item.id !== id)) // 删除
setList(list.map(item => item.id === id ? { ...item, name: 'New' } : item)) // 更新
💡 提示:可使用 Immer 简化不可变操作(但初学建议先掌握原生写法)
五、副作用处理:useEffect 全面解析
useEffect 是 React 中处理副作用(如数据获取、订阅、DOM 操作)的唯一 Hook。
1. 基本语法
useEffect(() => {
// 副作用逻辑(如 fetch、addEventListener)
return () => {
// 可选:清理函数(类似 beforeUnmount)
}
}, [dependencies]) // 依赖数组
2. 常见场景对照 Vue
| Vue | React |
|---|---|
onMounted | useEffect(() => {}, []) |
watch(xxx) | useEffect(() => {}, [xxx]) |
watchEffect | useEffect(() => {})(无依赖) |
beforeUnmount | useEffect(() => () => cleanup, []) |
3. 实战示例:数据获取
function UserProfile({ userId }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
setLoading(true)
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data)
setLoading(false)
})
}, [userId]) // userId 变化时重新请求
if (loading) return <div>Loading...</div>
return <div>{user.name}</div>
}
⚠️ 注意:不要在 useEffect 中直接使用 async 函数,应封装内部逻辑。
六、派生状态与性能优化:useMemo 与 useCallback
1. useMemo:缓存计算结果(类似 computed)
const expensiveValue = useMemo(() => {
return items.filter(item => item.active).map(item => item.name)
}, [items])
✅ 适用场景:开销大的计算、避免子组件不必要重渲染
2. useCallback:缓存函数引用
const handleClick = useCallback((id) => {
doSomething(id)
}, [doSomething]) // 依赖项变化才生成新函数
✅ 适用场景:传递给子组件的回调函数,避免因父组件重渲染导致子组件重渲染
3. 何时需要优化?
- 默认情况下 不需要过度优化
- 仅当出现性能问题(如列表卡顿)或子组件使用
React.memo时才考虑
七、组件通信模式详解
1. 父 → 子:Props
// Parent
<Child title="Hello" count={5} />
// Child
function Child({ title, count }) {
return <div>{title}: {count}</div>
}
2. 子 → 父:回调函数
// Parent
const handleDelete = (id) => {
setList(list.filter(item => item.id !== id))
}
<Item onDelete={handleDelete} />
// Item
<button onClick={() => props.onDelete(props.id)}>Delete</button>
3. 兄弟组件通信
- 方案1:状态提升(lift state up)到共同父组件
- 方案2:使用 Context 或 状态管理库
4. 跨层级通信:Context API(见第八节)
八、Context API 与全局状态管理
适用于中等复杂度的跨组件状态共享(如主题、用户信息)。
1. 创建 Context
// context/UserContext.js
import { createContext, useContext } from 'react'
const UserContext = createContext()
export function UserProvider({ children }) {
const [user, setUser] = useState(null)
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
)
}
export function useUser() {
const context = useContext(UserContext)
if (!context) throw new Error('useUser must be used within UserProvider')
return context
}
2. 使用
// main.jsx
import { UserProvider } from './context/UserContext'
ReactDOM.createRoot(root).render(
<UserProvider>
<App />
</UserProvider>
)
// 任意组件
const { user, setUser } = useUser()
⚠️ 注意:Context 适合低频更新的状态。高频更新(如鼠标坐标)会导致所有消费组件重渲染,此时应考虑 Zustand 等方案。
九、现代状态管理方案:Zustand 实战
Zustand 是轻量、简洁、无 Provider 的状态管理库,类似 Pinia。
1. 安装
npm install zustand
2. 创建 Store
// store/useCartStore.js
import { create } from 'zustand'
export const useCartStore = create((set, get) => ({
items: [],
total: 0,
addItem: (product) =>
set((state) => ({
items: [...state.items, product],
total: state.total + product.price
})),
removeItem: (id) =>
set((state) => {
const item = state.items.find(i => i.id === id)
return {
items: state.items.filter(i => i.id !== id),
total: state.total - item.price
}
}),
// 可以包含派生状态(getter)
getItemCount: () => get().items.length
}))
3. 在组件中使用
import { useCartStore } from './store/useCartStore'
function Cart() {
const { items, total, addItem } = useCartStore()
const itemCount = useCartStore(state => state.getItemCount())
return (
<div>
Items: {itemCount}, Total: ${total}
<button onClick={() => addItem({ id: 1, price: 10 })}>Add</button>
</div>
)
}
✅ 优势:
- 无需 Provider 包裹
- 自动选择性订阅(只监听用到的状态)
- 支持 middleware(persist, devtools 等)
十、路由系统:React Router v6 指南
1. 安装
npm install react-router-dom
2. 配置路由
// main.jsx
import {
createBrowserRouter,
RouterProvider,
} from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'
const router = createBrowserRouter([
{ path: '/', element: <Home /> },
{ path: '/about', element: <About /> },
{ path: '/users/:id', element: <User /> },
])
ReactDOM.createRoot(document.getElementById('root')).render(
<RouterProvider router={router} />
)
3. 页面内导航
import { Link, useNavigate, useParams } from 'react-router-dom'
function Nav() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
)
}
function UserProfile() {
const { id } = useParams() // 获取 URL 参数
const navigate = useNavigate()
const goBack = () => navigate(-1)
const goToHome = () => navigate('/')
return <div>User ID: {id}</div>
}
💡 React Router v6 强调 约定优于配置,嵌套路由、loader/action 等高级功能可后续学习。
十一、表单处理:受控组件与自定义 Hook
1. 受控组件(Controlled Components)
React 中表单元素的值由 state 控制:
function LoginForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const handleSubmit = (e) => {
e.preventDefault()
console.log({ email, password })
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<button type="submit">Login</button>
</form>
)
}
2. 自定义 useForm Hook(简化重复逻辑)
// hooks/useForm.js
import { useState } from 'react'
export function useForm(initialValues) {
const [values, setValues] = useState(initialValues)
const handleChange = (e) => {
const { name, value } = e.target
setValues(prev => ({ ...prev, [name]: value }))
}
return [values, handleChange, () => setValues(initialValues)]
}
使用:
function SignupForm() {
const [form, handleChange, reset] = useForm({ name: '', email: '' })
return (
<form>
<input name="name" value={form.name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />
<button type="button" onClick={reset}>Reset</button>
</form>
)
}
十二、常见陷阱与最佳实践
1. 状态更新是异步且批量的
setCount(count + 1)
setCount(count + 1) // ❌ 两次都基于旧 count,结果只 +1
// ✅ 正确:使用函数式更新
setCount(c => c + 1)
setCount(c => c + 1) // 结果 +2
2. useEffect 依赖数组必须完整
ESLint 插件 eslint-plugin-react-hooks 会自动检查。
3. 避免在渲染中创建新对象/函数
// ❌ 每次渲染都创建新对象,导致子组件重渲染
<Child style={{ color: 'red' }} />
// ✅ 提前定义或使用 useMemo
const childStyle = useMemo(() => ({ color: 'red' }), [])
<Child style={childStyle} />
4. 不要滥用 useRef 存储可变值
useRef 主要用于 DOM 引用或跨渲染保持可变值,不应替代状态。
十三、进阶建议与学习路径
学习路线图
- 基础:函数组件、JSX、useState、useEffect
- 进阶 Hook:useContext、useReducer、useMemo、useCallback
- 状态管理:Context → Zustand/Jotai
- 路由:React Router v6
- 数据获取:React Query(比 useEffect 更强大)
- 测试:Vitest + React Testing Library
- 性能优化:React.memo、lazy/Suspense、Profiler
- TypeScript:全面类型化组件与 Hook
推荐资源
- 官方文档(新版):react.dev
- 交互式教程:react.dev/learn
- 状态管理:Zustand GitHub
- 数据获取:React Query
- 社区:Reactiflux Discord
🎯 最后提醒:
React 的核心不是 API,而是 思维方式。
多写、多重构、多思考“UI 如何随状态变化”,你将很快掌握它!
祝你从 Vue 到 React 的转型之路顺利!如有具体问题,欢迎继续探讨。