React 是什么?
理解 React 的设计哲学、核心定位和为什么它改变了前端开发的格局
React 的诞生与定位
2013 年,Facebook(现 Meta)在 JSConf 上开源了一个名为 React 的 JavaScript 库。它的核心理念非常简单:UI = f(state)——将用户界面视为状态的函数映射。
这个看似简单的理念,彻底改变了前端开发的范式。在 React 之前,开发者需要手动操作 DOM 来更新界面(命令式编程);React 之后,开发者只需描述界面应该呈现什么样子,React 负责高效地更新 DOM(声明式编程)。
React 并不是第一个前端框架,但它引入了三个革命性的概念:组件化(将 UI 拆分为独立、可复用的组件)、虚拟 DOM(通过 diff 算法最小化 DOM 操作)、单向数据流(数据从父组件流向子组件,可预测、可调试)。这三个概念至今仍是现代前端框架的设计基础。
声明式编程
你只需描述「UI 应该长什么样」,React 负责高效更新 DOM。当数据变化时,React 自动重新渲染组件,你不需要手动操作 DOM 节点。
组件化架构
将复杂的 UI 拆分为独立、可复用的组件。每个组件管理自己的状态和渲染逻辑。组件可以嵌套组合,像乐高积木一样构建完整的界面。
虚拟 DOM
React 在内存中维护一个虚拟 DOM 树。状态变化时,React 先在虚拟 DOM 上计算差异(diff),然后只更新真实 DOM 中变化的部分,避免昂贵的全量 DOM 操作。
生态丰富
React 拥有全球最大的前端生态系统:Next.js(全栈框架)、React Native(移动开发)、Redux/Zustand(状态管理)、React Router(路由)。一次学习,全栈受益。
第一个 React 组件
让我们从一个简单的 HelloMessage 组件开始,理解 React 组件的基本结构。
// 最简单的 React 函数组件
function HelloMessage({ name }) {
return (
<div className="greeting">
<h1>Hello, {name}!</h1>
<p>Welcome to React</p>
</div>
);
}
// 使用组件
const root = ReactDOM.createRoot(
document.getElementById('root')
);
root.render(<HelloMessage name="Alice" />);
React 组件的核心概念:
- 函数组件:一个返回 JSX 的普通 JavaScript 函数
- Props({ name }):组件的输入参数,父组件传入的数据
- JSX:JavaScript 的语法扩展,可以在 JS 中写类 HTML 结构
- className:React 中用 className 替代 class(class 是 JS 保留字)
- createRoot:React 18 的新 API,创建渲染根节点
React 渲染流程:从状态到界面
理解 React 的渲染流程,是掌握 React 性能优化的基础。以下是 React 从状态变化到 DOM 更新的完整流程。
课程路线图
React 的核心理念 "UI = f(state)" 是什么意思?
React 的核心概念
深入理解组件、Props、State、Hooks 和生命周期——React 开发的五大基石
组件与 Props
组件是 React 的基本构建单元。它是一个返回 UI 元素的 JavaScript 函数(或类)。Props 是父组件传递给子组件的数据,实现组件间的通信。
好的组件遵循单一职责原则——每个组件只做一件事。如果组件变得复杂,就应该拆分为更小的子组件。理想情况下,一个组件的代码不超过 100 行。组件名使用 PascalCase(如 UserCard、ShoppingCart),与 HTML 元素区分。
// 父组件:组合多个子组件
function UserDashboard() {
const user = { name: "Alice", role: "admin" };
return (
<div className="dashboard">
{/* 数据通过 props 向下流动 */}
<UserHeader user={user} />
<UserStats userId={user.id} />
<UserActivity userId={user.id} />
</div>
);
}
// 子组件:接收 props 并渲染
function UserHeader({ user }) {
return (
<header>
<h1>{user.name}</h1>
<span className="badge">{user.role}</span>
</header>
);
}
组件与 Props 的核心模式:
- 组合模式:父组件包含子组件,形成组件树
- Props 传递:数据通过 props 从父组件流向子组件
- 解构赋值:({ user }) 直接从 props 中提取参数
- 单向数据流:子组件只读取 props,不修改
State 与 Hooks
如果说 Props 是组件的输入,那么 State 就是组件的内部记忆。Hooks 是 React 16.8 引入的 API,让函数组件也能使用状态和副作用等功能。
useState
最基础的 Hook。声明一个状态变量和更新函数。const [count, setCount] = useState(0)。状态更新触发重新渲染。支持函数式更新:setCount(prev => prev + 1)。
useEffect
处理副作用:API 调用、DOM 操作、事件监听、定时器。第二个参数是依赖数组,控制何时重新执行。空数组 [] 表示只在挂载时执行一次。返回清理函数处理资源释放。
useRef
持有可变引用,不触发重新渲染。常见用途:引用 DOM 元素(focus 管理)、存储上一次的值、存储不需要触发渲染的定时器 ID。
useMemo / useCallback
性能优化 Hook。useMemo 缓存计算结果,useCallback 缓存函数引用。避免不必要的重新计算和子组件重新渲染。但不要过度使用——缓存本身也有开销。
function Counter() {
const [count, setCount] = useState(0);
const [step, setStep] = useState(1);
// 副作用:每次 count 变化时更新标题
useEffect(() => {
document.title = `Count: ${count}`;
return () => { document.title = "React App"; };
}, [count]); // 依赖:只在 count 变化时执行
// 计算属性:缓存计算结果
const doubled = useMemo(() => count * 2, [count]);
return (
<div>
<p>Count: {count} | Doubled: {doubled}</p>
<button onClick={() => setCount(c => c + step)}>
Increment by {step}
</button>
</div>
);
}
Hooks 的使用模式:
- useState:声明状态,[value, setter] 解构
- useEffect:副作用处理,[count] 是依赖数组
- 清理函数:return 的函数在组件卸载或依赖变化前执行
- useMemo:只有 count 变化时才重新计算 doubled
- 函数式更新:setCount(c => c + step) 确保基于最新值更新
组件生命周期与 useEffect
理解组件的生命周期——从挂载到更新到卸载——是写出正确 React 代码的关键。在函数组件中,生命周期通过 useEffect 实现。
挂载(Mount)— 组件首次渲染
更新(Update)— 状态或 Props 变化
卸载(Unmount)— 组件销毁
React 中 Props 和 State 的关键区别是什么?
useEffect 的依赖数组为空 [] 时,副作用何时执行?
React 的实践应用
状态管理、表单处理、API 集成和路由——构建真实 React 应用的核心技能
状态管理:从本地到全局
React 应用的状态管理分为三个层次:组件本地状态(useState)、跨组件共享状态(Context)、全局状态管理(Zustand/Redux)。选择合适的层次,是 React 开发的重要决策。
不要过度工程化。大多数应用只需要 useState + Context。只有当状态逻辑非常复杂(如购物车、协作编辑、实时仪表盘)时,才需要引入 Zustand 或 Redux。 Zustand 是 2024 年最推荐的轻量级状态管理库。
// 简单的待办事项应用
function TodoApp() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState("");
const addTodo = useCallback(() => {
if (!input.trim()) return;
setTodos(prev => [...prev, {
id: Date.now(),
text: input,
completed: false
}]);
setInput("");
}, [input]);
const toggleTodo = useCallback((id) => {
setTodos(prev => prev.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
}, []);
return (
<div>
<input value={input}
onChange={e => setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addTodo()}
/>
<TodoList todos={todos} onToggle={toggleTodo} />
</div>
);
}
状态管理的核心模式:
- 不可变更新:[...prev, newItem] 创建新数组,不修改原数组
- useCallback:缓存函数引用,避免子组件不必要的重新渲染
- 展开运算符:{ ...todo, completed: !todo.completed } 更新单个属性
- 受控组件:input 的值由 state 控制,onChange 更新 state
表单处理与数据获取
表单是 Web 应用最常见的交互元素。React 的受控组件模式让表单状态管理变得清晰可预测。
function UserProfile() {
const [formData, setFormData] = useState({
name: "", email: "", bio: ""
});
const [status, setStatus] = useState("idle");
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = async (e) => {
e.preventDefault();
setStatus("submitting");
try {
await fetch('/api/profile', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
setStatus("success");
} catch (err) {
setStatus("error");
}
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={formData.name}
onChange={handleChange} />
<button disabled={status === "submitting"}>
{status === "submitting" ? "Saving..." : "Save"}
</button>
</form>
);
}
表单处理的关键模式:
- 统一状态对象:所有表单字段存储在一个对象中
- 动态属性名:[name]: value 根据字段名动态更新
- 提交状态管理:idle/submitting/success/error 四种状态
- 防重复提交:disabled={status === "submitting"}
API 数据获取模式
React 组件获取 API 数据是一个常见但需要正确处理的场景。自定义 Hook 是封装数据获取逻辑的最佳方式。
自定义 Hook:useFetch
封装 fetch 调用,返回 { data, loading, error }。支持自动重试、缓存和取消请求。将重复的数据获取逻辑抽象为可复用的 Hook。
SWR / React Query
生产级数据获取库。自动缓存、重新验证、乐观更新、分页、无限滚动。SWR 更轻量,React Query 功能更丰富。推荐新项目使用 SWR。
请求取消
使用 AbortController 在组件卸载时取消未完成的请求。避免"无法对已卸载的组件执行状态更新"的警告和内存泄漏。
在 React 中,为什么状态更新必须使用不可变方式(如展开运算符)?
React 的进阶主题
Context API、性能优化、自定义 Hook 和 React Server Components——高级开发者的必备技能
Context API:跨组件数据共享
当多个层级的组件需要共享同一份数据时(如主题、用户信息、语言设置),逐层传递 Props 会变得非常繁琐。Context API 解决了这个问题——让数据跨越组件层级直接传递。
// 1. 创建 Context
const ThemeContext = createContext('light');
const UserContext = createContext(null);
// 2. 在顶层组件提供数据
function App() {
return (
<ThemeContext.Provider value="dark">
<UserContext.Provider value={{ name: "Alice" }}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
// 3. 在任意深层组件中使用
function ThemedButton() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<button className={`btn btn-${theme}`}>
{user.name}'s Button
</button>
);
}
Context API 的三步使用模式:
- createContext:创建 Context 对象,参数是默认值
- Provider:在顶层包裹子组件树,提供共享数据
- useContext:在任意深层组件中直接访问数据,无需 Props 传递
- 多 Context:可以嵌套多个 Provider,管理不同的共享数据
React 性能优化
React 默认已经很高效,但在复杂应用中仍然需要针对性的优化。以下是 React 性能优化的核心策略。
React.memo
浅比较 Props,如果没变就跳过重新渲染。适合渲染开销大、Props 不经常变化的组件。const MemoComp = React.memo(ExpensiveComponent)。配合 useCallback 使用效果更好。
useMemo
缓存计算结果,避免每次渲染都重新计算。const sorted = useMemo(() => heavySort(data), [data])。适合排序、过滤等计算密集型操作。只在依赖变化时重新计算。
虚拟列表
渲染长列表时,只渲染可见区域的项目。使用 react-window 或 @tanstack/virtual。10000 条数据只渲染 20 条 DOM 节点,内存和性能都大幅优化。
代码分割
使用 React.lazy + Suspense 实现路由级代码分割。每个页面独立打包,按需加载。首屏加载体积减少 50%+,显著提升 FCP 和 LCP 指标。
// 优化示例:综合运用 memo + useMemo + useCallback
const ExpensiveList = React.memo(({ items, onSelect }) => {
// 只在 items 变化时重新排序
const sorted = useMemo(
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
return (
<ul>
{sorted.map(item => (
<li key={item.id} onClick={() => onSelect(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
// 父组件中缓存回调
function Parent() {
const [items, setItems] = useState([...]);
const handleSelect = useCallback((id) => {
console.log('Selected:', id);
}, []); // 空依赖 → 函数引用不变
return <ExpensiveList items={items} onSelect={handleSelect} />;
}
性能优化的组合使用:
- React.memo:浅比较 Props,跳过不必要的重新渲染
- useMemo:缓存排序结果,只在 items 变化时重新计算
- useCallback:缓存函数引用,配合 memo 使用
- 三者配合:memo 检测 Props 变化 + useMemo 缓存数据 + useCallback 缓存函数
React Server Components
React Server Components(RSC)是 React 架构的重大演进。它允许组件在服务端运行,直接访问数据库和文件系统,同时保持 React 的组件化开发体验。
Server Components:在服务端渲染,可以直连数据库、读取文件,不发送 JS 到客户端。Client Components:在客户端运行,可以使用 useState、useEffect、事件处理。通过 'use client' 指令标记。两者可以混合使用,Server Components 可以渲染 Client Components。
React.memo 的作用是什么?
React Server Components 的主要优势是什么?
总结与练习
回顾 React 核心知识,通过综合练习巩固技能,探索 React 生态系统
核心知识回顾
组件化架构
UI = f(state)。函数组件返回 JSX,Props 传递数据,State 管理内部状态。组件是独立、可复用的 UI 构建单元。遵循单一职责,每个组件不超过 100 行。
Hooks 体系
useState(状态)、useEffect(副作用)、useRef(引用)、useMemo(缓存计算)、useCallback(缓存函数)。Hooks 让函数组件拥有完整的 React 能力。遵循 Hooks 规则:只在顶层调用,不在条件语句中使用。
性能优化
React.memo + useMemo + useCallback 三件套。虚拟列表处理长列表。代码分割按需加载。不可变状态更新确保变化检测。先测量(React DevTools Profiler),再优化。
生态系统
Next.js(全栈框架)、React Router(路由)、Zustand(状态管理)、SWR(数据获取)、React Query(服务端状态)、React Native(移动开发)。按需选择,不要过度引入依赖。
React 开发最佳实践
综合练习:构建任务管理应用
设计一个功能完整的任务管理应用,综合运用组件化、状态管理、性能优化和 API 集成。
构建一个任务管理应用,具备以下功能:1) 添加、编辑、删除任务;2) 任务筛选(全部/进行中/已完成);3) 搜索功能;4) 数据持久化(localStorage);5) 性能优化(memo + useMemo)。
function TaskManager() {
// TODO 1: 定义任务列表状态
// TODO 2: 定义筛选条件状态
// TODO 3: 定义搜索关键词状态
// TODO 4: 实现添加任务
const addTask = (text) => { /* ??? */ };
// TODO 5: 实现切换完成状态
const toggleTask = (id) => { /* ??? */ };
// TODO 6: 用 useMemo 缓存筛选结果
const filteredTasks = /* useMemo(...) */;
return (
<div>
<TaskInput onAdd={addTask} />
<TaskFilter value={filter} onChange={setFilter} />
<TaskList tasks={filteredTasks} onToggle={toggleTask} />
</div>
);
}
完成这个练习的建议:
- TODO 1-3:三个 useState 分别管理任务列表、筛选和搜索
- TODO 4:setTasks(prev => [...prev, newTask])
- TODO 5:map + 展开运算符更新单个任务
- TODO 6:useMemo 缓存 filter + search 的组合筛选结果
下一步学习方向
Next.js 全栈框架
React 的官方推荐框架。Server Components、App Router、API Routes、SSR/SSG 一体化。学习 Next.js 是 React 开发者的自然下一步。
React Native
使用 React 语法开发原生 iOS 和 Android 应用。一次学习,全平台开发。Expo 框架让 React Native 开发更加简单。
React + AI
将 AI 能力集成到 React 应用中:流式输出(SSE)、AI 对话界面、智能表单、内容生成。Vercel AI SDK 是目前最流行的 React AI 集成方案。
React 的主要特点包括哪些?
React 性能优化的正确顺序是什么?
React 中处理副作用的 Hook 是什么?