# 📘 React 深度入门教程(专为 Vue 开发者定制)

46 阅读5分钟

这仍然是一篇AI文章,足够幸运地生活在这个AI时代,知识的获取从未变得如此容易。

作者:Qwen3-Max
目标读者:熟悉 Vue 2/3 的前端开发者,希望系统掌握 React 核心概念与开发范式


目录


一、React 与 Vue 的核心哲学差异

维度VueReact
响应式机制自动依赖追踪(Proxy / defineProperty)手动触发重渲染(通过状态 setter)
UI 更新模型数据变化 → 自动更新 DOM状态变化 → 重新执行组件函数 → Virtual DOM diff → 更新 DOM
模板语言<template> + 指令(v-if, v-forJSX: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.js
  • App.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 语法规则

VueReact (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

VueReact
onMounteduseEffect(() => {}, [])
watch(xxx)useEffect(() => {}, [xxx])
watchEffectuseEffect(() => {})(无依赖)
beforeUnmountuseEffect(() => () => 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 引用或跨渲染保持可变值,不应替代状态


十三、进阶建议与学习路径

学习路线图

  1. 基础:函数组件、JSX、useState、useEffect
  2. 进阶 Hook:useContext、useReducer、useMemo、useCallback
  3. 状态管理:Context → Zustand/Jotai
  4. 路由:React Router v6
  5. 数据获取:React Query(比 useEffect 更强大)
  6. 测试:Vitest + React Testing Library
  7. 性能优化:React.memo、lazy/Suspense、Profiler
  8. TypeScript:全面类型化组件与 Hook

推荐资源


🎯 最后提醒
React 的核心不是 API,而是 思维方式
多写、多重构、多思考“UI 如何随状态变化”,你将很快掌握它!

祝你从 Vue 到 React 的转型之路顺利!如有具体问题,欢迎继续探讨。