React框架
React 是前端三大框架之一,由 Meta(Facebook)开源。声明式编程、组件化架构、虚拟 DOM 是其三大核心特征。本文档系统覆盖 React 从入门到进阶的全部核心知识,适合学习和复习使用。
一、React 概述
1.1 React 是什么?
React 是一个用于构建用户界面的 JavaScript 库。它的核心思想是:
声明式:你描述 UI 应该长什么样,React 负责高效更新 DOM。 组件化:将 UI 拆分为独立、可复用的组件,每个组件管理自己的状态。 一次学习,随处编写:React 可以渲染到 Web、移动端(React Native)、服务端(SSR)等多种平台。
虚拟 DOM 原理
React 在内存中维护一棵虚拟 DOM 树。状态变化时,React 通过 Diff 算法 对比新旧虚拟 DOM,计算出最小更新操作,再批量应用到真实 DOM。这避免了频繁操作真实 DOM 带来的性能问题。
状态变化 → 生成新虚拟DOM → Diff对比 → 计算最小变更 → 批量更新真实DOM
1.2 React vs Vue 全面对比
|
维度 |
React |
Vue |
|---|---|---|
|
开发者 |
Meta(Facebook) |
Evan You + 社区 |
|
定位 |
UI 库(需搭配第三方方案) |
渐进式框架(内置路由/状态管理) |
|
编程范式 |
函数式编程为主 |
声明式 + 响应式 |
|
模板语法 |
JSX(JavaScript + XML) |
SFC 单文件组件(template/script/style) |
|
响应式原理 |
手动 setState / Hooks 触发更新 |
自动依赖追踪(Proxy / getter-setter) |
|
组件通信 |
Props 向下 + Callback 向上 + Context |
Props 向下 + Emit 向上 + Provide/Inject |
|
状态管理 |
Redux / Zustand / Jotai |
Pinia / Vuex |
|
路由 |
React Router |
Vue Router(官方维护) |
|
TypeScript 支持 |
优秀(天然 JSX = TSX) |
良好(3.0+ 全面支持) |
|
学习曲线 |
较陡(JSX、Hooks 心智模型) |
平缓(模板语法接近 HTML) |
|
生态 |
极丰富,社区庞大 |
丰富,官方维护核心库 |
|
适用场景 |
大型应用、跨平台、生态灵活性要求高 |
中小型到大型应用、快速开发 |
选型建议:大型项目、需要跨平台(React Native)选 React;追求开发效率、渐进式迁移选 Vue。两者没有绝对优劣,取决于团队和场景。
二、环境搭建
2.1 使用 Vite 创建项目(推荐)
# 创建项目
npm create vite@latest my-react-app -- --template react
# 进入项目并安装依赖
cd my-react-app
npm install
# 启动开发服务器
npm run dev
2.2 项目目录结构
my-react-app/
├── index.html # 入口 HTML
├── package.json # 依赖与脚本
├── vite.config.js # Vite 配置
├── src/
│ ├── main.jsx # 应用入口(挂载根组件)
│ ├── App.jsx # 根组件
│ ├── App.css # 根组件样式
│ └── components/ # 组件目录
├── public/ # 静态资源
2.3 JSX 语法入门
JSX 是 JavaScript 的语法扩展,让你在 JS 中写类似 HTML 的代码。
// JSX 基本语法
const element = <h1>Hello, React!</h1>;
// 表达式用 {} 包裹
const name = '张三';
const greeting = <p>你好,{name}</p>;
// 条件渲染
const isLoggedIn = true;
const status = isLoggedIn ? <span>已登录</span> : <span>未登录</span>;
// 列表渲染
const items = ['Apple', 'Banana', 'Cherry'];
const list = (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
// 样式处理
const styleObj = { color: 'red', fontSize: '16px' };
const styled = <div style={styleObj}>内联样式</div>;
const classed = <div className="container">CSS 类名</div>;
JSX 注意点:class → className、for → htmlFor、style 接受 JS 对象而非字符串、所有标签必须闭合(包括 <br/>、<img/>)、return 只能返回一个根元素(可用 <></> Fragment 包裹)。
三、组件化开发
3.1 函数组件(推荐)
React 16.8 引入 Hooks 后,函数组件成为主流写法。
// 函数组件 — 接收 props,返回 JSX
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// 箭头函数写法
const Welcome = ({ name, age = 18 }) => {
return (
<div>
<h1>Hello, {name}!</h1>
<p>年龄: {age}</p>
</div>
);
};
// 使用组件
<Welcome name="张三" age={25} />
3.2 类组件(了解)
import React, { Component } from 'react';
class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
函数组件 vs 类组件:函数组件更简洁、性能更好(无需实例化)、Hooks 让函数组件拥有类组件的全部能力。新项目一律推荐函数组件 + Hooks。
3.3 Props 传递与组件复用
// 父组件
function Parent() {
const user = { name: '张三', role: 'admin' };
return (
<div>
{/* Props 传递:原始值、对象、函数、JSX */}
<Child
name={user.name}
user={user}
onConfirm={() => console.log('确认')}
footer={<button>提交</button>}
/>
</div>
);
}
// 子组件 — Props 是只读的,不可修改
function Child({ name, user, onConfirm, footer }) {
return (
<div>
<h2>{name}</h2>
<p>角色: {user.role}</p>
<button onClick={onConfirm}>确定</button>
{footer}
</div>
);
}
3.4 事件处理
function EventDemo() {
const handleClick = (e) => {
e.preventDefault(); // 阻止默认行为
e.stopPropagation(); // 阻止冒泡
console.log('按钮被点击');
};
const handleChange = (e) => {
console.log('输入值:', e.target.value);
};
return (
<div>
<button onClick={handleClick}>点击我</button>
<input onChange={handleChange} placeholder="输入内容" />
</div>
);
}
四、组件通信
4.1 父子通信(最常用)
// 父 → 子:通过 Props 传递数据
// 子 → 父:通过回调函数传递数据
function Parent() {
const [count, setCount] = useState(0);
return (
<Child
count={count} // 父 → 子
onIncrement={() => setCount(count + 1)} // 子 → 父
/>
);
}
function Child({ count, onIncrement }) {
return (
<div>
<p>当前计数: {count}</p>
<button onClick={onIncrement}>+1</button>
</div>
);
}
4.2 兄弟通信(状态提升)
// 将共享状态提升到最近的公共父组件
function Parent() {
const [sharedData, setSharedData] = useState('');
return (
<div>
<SiblingA onUpdate={setSharedData} />
<SiblingB data={sharedData} />
</div>
);
}
4.3 跨层级通信(Context)
import { createContext, useContext, useState } from 'react';
// 1. 创建 Context
const ThemeContext = createContext('light');
// 2. 顶层提供数据
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Page />
</ThemeContext.Provider>
);
}
// 3. 深层组件消费数据(无需通过中间组件传递)
function DeepChild() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<div>
<p>当前主题: {theme}</p>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
切换主题
</button>
</div>
);
}
4.4 通信方式总结
|
场景 |
方案 |
适用情况 |
|---|---|---|
|
父→子 |
Props |
直接父子关系 |
|
子→父 |
回调函数 |
直接父子关系 |
|
兄弟组件 |
状态提升 |
共享父组件较近 |
|
跨层级 |
Context / Redux / Zustand |
深层嵌套或多组件共享 |
|
非关联组件 |
全局状态管理 / 事件总线 |
组件间无直接关系 |
五、React Hooks(核心)
5.1 useState — 状态管理
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // 基本类型
const [user, setUser] = useState({ // 对象类型
name: '张三',
age: 25
});
const updateUser = () => {
// 更新对象时需要使用展开运算符保留旧值
setUser(prev => ({ ...prev, age: prev.age + 1 }));
};
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(c => c - 1)}>-1</button>
<p>{user.name} - {user.age}岁</p>
<button onClick={updateUser}>年龄+1</button>
</div>
);
}
注意:setState 是异步的,多次调用会批处理。需要依赖前值请使用函数式更新 setCount(prev => prev + 1)。
5.2 useEffect — 副作用处理
import { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// 副作用:数据获取、订阅、DOM操作等
const fetchData = async () => {
try {
const res = await fetch('https://api.example.com/data');
const json = await res.json();
setData(json);
} finally {
setLoading(false);
}
};
fetchData();
// 清理函数(组件卸载或依赖变化时执行)
return () => {
console.log('清理副作用');
};
}, []); // 空数组 = 仅在挂载时执行一次
if (loading) return <div>加载中...</div>;
return <div>{JSON.stringify(data)}</div>;
}
useEffect 依赖项说明
|
依赖项 |
执行时机 |
|---|---|
|
|
仅在组件挂载时执行一次(componentDidMount) |
|
|
挂载时 + a 或 b 变化时执行 |
|
不传第二个参数 |
每次渲染都执行 |
5.3 useRef — 引用与持久化
import { useRef, useEffect } from 'react';
function InputFocus() {
const inputRef = useRef(null); // DOM 元素引用
const countRef = useRef(0); // 持久化值(不触发重渲染)
useEffect(() => {
inputRef.current.focus(); // 自动聚焦输入框
}, []);
const handleClick = () => {
countRef.current += 1;
console.log('点击次数:', countRef.current);
};
return (
<div>
<input ref={inputRef} placeholder="自动聚焦" />
<button onClick={handleClick}>点击计数(不触发渲染)</button>
</div>
);
}
useRef vs useState:useRef 修改 .current 不会触发重渲染,适合存储 DOM 引用、定时器 ID、保存不需要响应式展示的值。
5.4 useMemo — 缓存计算结果
import { useMemo, useState } from 'react';
function ExpensiveList({ items, filter }) {
// 仅在 items 或 filter 变化时才重新计算
const filteredItems = useMemo(() => {
console.log('重新过滤...');
return items.filter(item => item.includes(filter));
}, [items, filter]);
return (
<ul>
{filteredItems.map(item => <li key={item}>{item}</li>)}
</ul>
);
}
5.5 useCallback — 缓存回调函数
import { useCallback, useState } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// 函数引用保持不变(除非 count 变化),避免子组件不必要的重渲染
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return <Child onClick={handleClick} />;
}
// 配合 React.memo 使用效果最佳
const Child = React.memo(({ onClick }) => {
console.log('Child 渲染');
return <button onClick={onClick}>点击</button>;
});
5.6 useReducer — 复杂状态管理
import { useReducer } from 'react';
// Reducer 函数
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
useState vs useReducer:简单状态用 useState;多个相关状态或复杂的更新逻辑用 useReducer。useReducer 和 Redux 的模式类似,适合在小型场景替代 Redux。
5.7 自定义 Hook — 复用逻辑
// 自定义 Hook:封装可复用的逻辑
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight });
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// 使用
function App() {
const { width, height } = useWindowSize();
return <p>窗口大小: {width} x {height}</p>;
}
5.8 Hooks 使用规则
- 只在函数组件或自定义 Hook 中调用(不在普通 JS 函数、类组件中调用)
- 只在顶层调用(不在循环、条件、嵌套函数中调用)
- 自定义 Hook 必须以
use开头命名
六、React Router 路由
6.1 安装与基本配置
npm install react-router-dom
// main.jsx — 根配置
import { BrowserRouter } from 'react-router-dom';
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
6.2 路由定义(v6)
import { Routes, Route, Link, NavLink } from 'react-router-dom';
function App() {
return (
<div>
<nav>
{/* Link: 声明式导航 */}
<Link to="/">首页</Link>
{/* NavLink: 激活时自动添加 active 类名 */}
<NavLink to="/about"
className={({ isActive }) => isActive ? 'active' : ''}>
关于
</NavLink>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:id" element={<User />} /> {/* 动态路由 */}
<Route path="*" element={<NotFound />} /> {/* 404 */}
</Routes>
</div>
);
}
6.3 动态路由与参数获取
import { useParams, useSearchParams } from 'react-router-dom';
function User() {
const { id } = useParams(); // 路径参数 /user/123
const [searchParams] = useSearchParams(); // 查询参数 /user/123?tab=info
return (
<div>
<p>用户 ID: {id}</p>
<p>当前 Tab: {searchParams.get('tab')}</p>
</div>
);
}
6.4 编程式导航
import { useNavigate } from 'react-router-dom';
function LoginButton() {
const navigate = useNavigate();
const handleLogin = async () => {
// 执行登录逻辑...
const success = true;
if (success) {
navigate('/dashboard'); // 跳转
// navigate('/dashboard', { replace: true }); // 替换(不可返回)
// navigate(-1); // 返回上一页
}
};
return <button onClick={handleLogin}>登录</button>;
}
6.5 嵌套路由
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} /> {/* index = 默认子路由 */}
<Route path="products" element={<Products />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
);
}
// Layout 组件中通过 <Outlet/> 渲染子路由
function Layout() {
return (
<div>
<header>导航栏</header>
<Outlet /> {/* 子路由内容在此渲染 */}
<footer>底部</footer>
</div>
);
}
6.6 路由守卫
function ProtectedRoute({ children }) {
const isLoggedIn = checkAuth(); // 检查登录状态
if (!isLoggedIn) {
return <Navigate to="/login" replace />;
}
return children;
}
// 使用
<Route path="/dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
七、状态管理
7.1 Context API(轻量方案)
// 适合主题、语言、用户信息等全局但简单的状态
const AuthContext = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (userData) => setUser(userData);
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
7.2 Redux Toolkit(中大型应用推荐)
npm install @reduxjs/toolkit react-redux
// store/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; },
decrement: (state) => { state.value -= 1; },
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
export const store = configureStore({
reducer: {
counter: counterReducer
}
});
// 在组件中使用
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './store/counterSlice';
function Counter() {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<p>{count}</p>
<button onClick={() => dispatch(increment())}>+1</button>
</div>
);
}
7.3 Zustand(轻量替代)
npm install zustand
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}));
// 组件中使用
function Counter() {
const count = useStore(state => state.count);
const increment = useStore(state => state.increment);
return (
<div>
<p>{count}</p>
<button onClick={increment}>+1</button>
</div>
);
}
7.4 状态管理方案对比
|
方案 |
包大小 |
学习曲线 |
适用场景 |
|---|---|---|---|
|
Context + useReducer |
0(内置) |
低 |
小型应用、主题/语言等全局配置 |
|
Zustand |
~1KB |
低 |
中小型应用、快速开发 |
|
Redux Toolkit |
~12KB |
中 |
中大型应用、团队协作、需要 DevTools |
|
Jotai / Recoil |
~2KB |
低 |
原子化状态管理、细粒度更新 |
八、性能优化
8.1 React.memo — 避免不必要的重渲染
const Child = React.memo(({ name }) => {
console.log('仅在 name 变化时渲染');
return <p>{name}</p>;
});
8.2 useMemo / useCallback
// useMemo: 缓存计算结果
const sortedList = useMemo(() => {
return list.sort((a, b) => a.name.localeCompare(b.name));
}, [list]);
// useCallback: 缓存函数引用(配合 React.memo)
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
8.3 懒加载(lazy + Suspense)
import { lazy, Suspense } from 'react';
// 组件按需加载,减少首屏 bundle 体积
const UserProfile = lazy(() => import('./UserProfile'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<Routes>
<Route path="/profile" element={<UserProfile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
8.4 优化策略总结
|
策略 |
说明 |
|---|---|
|
React.memo |
浅比较 Props,未变化时跳过渲染 |
|
useMemo |
缓存计算开销大的结果 |
|
useCallback |
稳定函数引用,配合 memo 使用 |
|
lazy + Suspense |
代码分割,按需加载 |
|
key 的正确使用 |
避免无序或重复的 key |
|
虚拟列表 |
长列表用 react-window / react-virtuoso |
九、最佳实践
9.1 受控组件 vs 非受控组件
// 受控组件:表单数据由 React state 管理(推荐)
function ControlledForm() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}
// 非受控组件:表单数据由 DOM 自身管理(特殊场景)
function UncontrolledForm() {
const inputRef = useRef(null);
const handleSubmit = () => {
console.log(inputRef.current.value);
};
return <input ref={inputRef} defaultValue="" />;
}
9.2 错误边界(Error Boundary)
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h1>出错了,请刷新页面重试。</h1>;
}
return this.props.children;
}
}
// 包裹可能出错的组件
<ErrorBoundary>
<RiskyComponent />
</ErrorBoundary>
9.3 常见反模式(应避免)
避免以下写法:
- 在 render 中直接修改 state(
state.count++而非setCount) - 在 useEffect 中遗漏依赖项(或用 eslint-disable 注释掩盖问题)
- 过度使用 useCallback / useMemo(只在真正需要性能优化时使用)
- 使用 index 作为 key(列表会增删排序时)
- 在条件语句 / 循环中调用 Hooks
- 单个组件承担过多职责(超过 200 行考虑拆分)
9.4 组件设计原则
- 单一职责:每个组件只做一件事
- 可复用性:通过 Props 让组件适应不同场景
- 组合优于继承:React 推崇组合模式
- 状态最小化:状态放在需要它的最近的公共祖先
- Props 向下,事件向上:数据单向流动
学习路径建议:JSX 基础 → 函数组件 + Props → useState/useEffect → 条件/列表渲染 → 组件通信 → useRef/useMemo/useCallback → React Router → 状态管理(Context → Redux Toolkit) → 性能优化 → 项目实战。
掌握以上内容即可应对日常开发 90% 的场景。关键是多写代码,在实践中理解每个概念。
9.5 脚手架项目
npx create-react-app 项目名称
或者
npm create vite@latest 项目名称 -- --template react
以上两种方式都可以快速创建一个React项目。推荐使用Vite,更快更轻量。