前置要求:HTML/CSS/JavaScript基础、ES6+语法
学习时长:约3-5天(每天4小时)
React版本:18+(Hooks)
适用场景:大型单页应用、移动端(React Native)、企业级项目
一、React 核心概念
| 概念 | 说明 |
|---|
| JSX | JavaScript + XML,描述UI |
| 组件 | 独立可复用的UI单元 |
| Hooks | 函数组件的状态和生命周期 |
| 单向数据流 | 数据自上而下流动 |
| 虚拟DOM | 高效的DOM更新机制 |
二、项目创建
# 使用Vite创建(推荐)
npm create vite@latest my-react-app -- --template react
# 进入项目
cd my-react-app
npm install
npm run dev
# 项目结构
my-react-app/
├── public/
├── src/
│ ├── components/ # 组件
│ ├── hooks/ # 自定义Hooks
│ ├── pages/ # 页面
│ ├── services/ # API服务
│ ├── stores/ # 状态管理
│ ├── utils/ # 工具函数
│ ├── App.jsx # 根组件
│ ├── main.jsx # 入口文件
│ └── index.css # 全局样式
├── index.html
├── vite.config.js
└── package.json
三、JSX 语法
function App() {
const name = '张三'
const age = 25
const isLoggedIn = true
const items = ['苹果', '香蕉', '橙子']
return (
<div className="app">
{/* 变量 */}
<h1>你好,{name}!</h1>
<p>年龄:{age}</p>
{/* 表达式 */}
<p>明年:{age + 1}</p>
<p>状态:{isLoggedIn ? '已登录' : '未登录'}</p>
{/* 列表 */}
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
{/* 条件渲染 */}
{isLoggedIn && <p>欢迎回来!</p>}
{/* 样式 */}
<div style={{ color: 'red', fontSize: '16px' }}>
内联样式
</div>
</div>
)
}
四、Hooks 详解
4.1 useState
import { useState } from 'react'
function Counter() {
// 基本用法
const [count, setCount] = useState(0)
// 对象状态
const [user, setUser] = useState({
name: '张三',
age: 25
})
// 数组状态
const [items, setItems] = useState([])
// 更新对象(需要展开)
const updateName = () => {
setUser(prev => ({ ...prev, name: '李四' }))
}
// 添加数组项
const addItem = () => {
setItems(prev => [...prev, `item-${Date.now()}`])
}
// 删除数组项
const removeItem = (index) => {
setItems(prev => prev.filter((_, i) => i !== index))
}
return (
<div>
<p>计数:{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(prev => prev + 1)}>+1 (函数式)</button>
</div>
)
}
4.2 useEffect
import { useState, useEffect } from 'react'
function UserProfile({ userId }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
// 组件挂载和userId变化时执行
useEffect(() => {
let cancelled = false
const fetchUser = async () => {
setLoading(true)
try {
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
if (!cancelled) {
setUser(data)
}
} finally {
if (!cancelled) {
setLoading(false)
}
}
}
fetchUser()
// 清理函数
return () => {
cancelled = true
}
}, [userId]) // 依赖数组
// 仅在挂载时执行
useEffect(() => {
console.log('组件已挂载')
return () => console.log('组件已卸载')
}, [])
if (loading) return <div>加载中...</div>
if (!user) return <div>用户不存在</div>
return <div>{user.name}</div>
}
4.3 自定义Hooks
// hooks/useFetch.js
import { useState, useEffect } from 'react'
export function useFetch(url) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
let cancelled = false
const fetchData = async () => {
setLoading(true)
setError(null)
try {
const response = await fetch(url)
if (!response.ok) throw new Error('请求失败')
const result = await response.json()
if (!cancelled) setData(result)
} catch (err) {
if (!cancelled) setError(err.message)
} finally {
if (!cancelled) setLoading(false)
}
}
fetchData()
return () => { cancelled = true }
}, [url])
return { data, loading, error }
}
// hooks/useDebounce.js
import { useState, useEffect } from 'react'
export function useDebounce(value, delay = 300) {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => clearTimeout(timer)
}, [value, delay])
return debouncedValue
}
// hooks/useLocalStorage.js
import { useState } from 'react'
export function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch {
return initialValue
}
})
const setValue = (value) => {
const valueToStore = value instanceof Function ? value(storedValue) : value
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
}
return [storedValue, setValue]
}
// 使用自定义Hooks
import { useFetch } from '../hooks/useFetch'
import { useDebounce } from '../hooks/useDebounce'
function UserList() {
const [keyword, setKeyword] = useState('')
const debouncedKeyword = useDebounce(keyword)
const { data: users, loading } = useFetch(`/api/users?keyword=${debouncedKeyword}`)
return (
<div>
<input value={keyword} onChange={e => setKeyword(e.target.value)} />
{loading ? '加载中...' : users?.map(u => <div key={u.id}>{u.name}</div>)}
</div>
)
}
五、组件通信
5.1 Props
// 父组件
function Parent() {
const [count, setCount] = useState(0)
return (
<Child
title="标题"
count={count}
onIncrement={() => setCount(prev => prev + 1)}
/>
)
}
// 子组件
function Child({ title, count, onIncrement }) {
return (
<div>
<h2>{title}</h2>
<p>计数:{count}</p>
<button onClick={onIncrement}>+1</button>
</div>
)
}
5.2 Context
// 创建Context
const ThemeContext = createContext('light')
// 提供者
function App() {
const [theme, setTheme] = useState('light')
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Layout />
</ThemeContext.Provider>
)
}
// 消费者
function Header() {
const { theme, setTheme } = useContext(ThemeContext)
return (
<header className={theme}>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
切换主题
</button>
</header>
)
}
六、React Router
npm install react-router-dom
// App.jsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="users" element={<UserList />} />
<Route path="users/:id" element={<UserDetail />} />
<Route path="admin" element={<ProtectedRoute><Admin /></ProtectedRoute>}>
<Route index element={<Dashboard />} />
<Route path="users" element={<AdminUsers />} />
</Route>
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</BrowserRouter>
)
}
// 布局组件
import { Outlet, NavLink } from 'react-router-dom'
function Layout() {
return (
<div>
<nav>
<NavLink to="/" className={({ isActive }) => isActive ? 'active' : ''}>
首页
</NavLink>
<NavLink to="/users">用户</NavLink>
<NavLink to="/admin">后台</NavLink>
</nav>
<Outlet /> {/* 子路由渲染位置 */}
</div>
)
}
// 获取路由参数
import { useParams, useNavigate, useSearchParams } from 'react-router-dom'
function UserDetail() {
const { id } = useParams() // 路径参数
const navigate = useNavigate() // 编程式导航
const [searchParams] = useSearchParams() // 查询参数
const page = searchParams.get('page')
return (
<div>
<p>用户ID:{id}</p>
<p>页码:{page}</p>
<button onClick={() => navigate('/')}>返回首页</button>
<button onClick={() => navigate(-1)}>返回上一页</button>
</div>
)
}
// 路由守卫
function ProtectedRoute({ children }) {
const token = localStorage.getItem('token')
if (!token) {
return <Navigate to="/login" replace />
}
return children
}
七、状态管理(Zustand)
npm install zustand
// stores/useUserStore.js
import { create } from 'zustand'
const useUserStore = create((set, get) => ({
// State
userInfo: null,
token: localStorage.getItem('token') || '',
// Getters(通过函数实现)
isLoggedIn: () => !!get().token,
displayName: () => get().userInfo?.nickname || get().userInfo?.username || '未登录',
// Actions
login: async (credentials) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
const data = await response.json()
set({ token: data.token, userInfo: data.user })
localStorage.setItem('token', data.token)
},
fetchUser: async () => {
const response = await fetch('/api/user', {
headers: { Authorization: `Bearer ${get().token}` }
})
const user = await response.json()
set({ userInfo: user })
},
logout: () => {
set({ token: '', userInfo: null })
localStorage.removeItem('token')
}
}))
export default useUserStore
// 使用Store
import useUserStore from '../stores/useUserStore'
function Header() {
const { userInfo, isLoggedIn, logout } = useUserStore()
return (
<header>
{isLoggedIn() ? (
<>
<span>{userInfo?.username}</span>
<button onClick={logout}>退出</button>
</>
) : (
<a href="/login">登录</a>
)}
</header>
)
}
八、表单处理
import { useState } from 'react'
function UserForm({ onSubmit }) {
const [form, setForm] = useState({
username: '',
email: '',
role: 'user'
})
const [errors, setErrors] = useState({})
const [loading, setLoading] = useState(false)
const handleChange = (e) => {
const { name, value } = e.target
setForm(prev => ({ ...prev, [name]: value }))
// 清除错误
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: '' }))
}
}
const validate = () => {
const newErrors = {}
if (!form.username) {
newErrors.username = '用户名不能为空'
} else if (form.username.length < 3) {
newErrors.username = '用户名至少3个字符'
}
if (!form.email) {
newErrors.email = '邮箱不能为空'
} else if (!/\S+@\S+\.\S+/.test(form.email)) {
newErrors.email = '邮箱格式不正确'
}
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
const handleSubmit = async (e) => {
e.preventDefault()
if (!validate()) return
setLoading(true)
try {
await onSubmit(form)
} finally {
setLoading(false)
}
}
return (
<form onSubmit={handleSubmit}>
<div>
<label>用户名:</label>
<input
name="username"
value={form.username}
onChange={handleChange}
/>
{errors.username && <span className="error">{errors.username}</span>}
</div>
<div>
<label>邮箱:</label>
<input
name="email"
type="email"
value={form.email}
onChange={handleChange}
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<div>
<label>角色:</label>
<select name="role" value={form.role} onChange={handleChange}>
<option value="user">普通用户</option>
<option value="admin">管理员</option>
</select>
</div>
<button type="submit" disabled={loading}>
{loading ? '提交中...' : '提交'}
</button>
</form>
)
}
九、常用UI库
| 组件库 | 适用场景 | 特点 |
|---|
| Ant Design | 企业级后台 | 组件最全、设计规范 |
| Material UI | Material风格 | Google设计语言 |
| Chakra UI | 现代应用 | 简洁灵活 |
| Shadcn/ui | 自定义风格 | 可复制组件代码 |
| Headless UI | 无样式组件 | 完全自定义样式 |
十、Vue vs React 对比
| 特性 | Vue 3 | React |
|---|
| 模板语法 | <template> | JSX |
| 状态管理 | ref() / reactive() | useState() |
| 副作用 | watchEffect() | useEffect() |
| 全局状态 | Pinia | Zustand/Redux |
| 路由 | Vue Router | React Router |
| 学习曲线 | ★★☆☆☆ | ★★★☆☆ |
| 中文生态 | ★★★★★ | ★★★☆☆ |
| 全球市场 | ★★★★☆ | ★★★★★ |
学习建议
- 先掌握JSX语法,理解React的思维方式
- 深入理解Hooks,特别是useState和useEffect
- 学会自定义Hooks,逻辑复用的关键
- 掌握React Router,单页应用必备
- 学习状态管理,Zustand是目前最推荐的方案
下一步学习