React面试宝典【Part6:状态管理生态】
2026-08-03
1. Redux 核心原理
核心思想:单向数据流
严格的闭环流程:
- View 触发 → dispatch(action)
- action 进入中间件链(异步/日志等)
- reducer 接收 (state, action),纯函数计算新 state
- store 更新 → 订阅组件重新渲染
三大原则
- 单一数据源:一个应用一个 store
- state 只读:只能通过 dispatch(action) 修改
- reducer 必须是纯函数:无副作用、相同输入必相同输出
中间件原理
- 本质:包装、增强 dispatch
- 在 action 到达 reducer 之前做拦截处理
- 典型:
- redux-thunk:让 action 可以是函数,支持异步
- redux-saga:用 generator 管理复杂异步流
- redux-logger:打印 action/state 变化
现状
- 优势:可预测、时间旅行调试、生态极全
- 劣势:样板代码多、写法繁琐
- 趋势:中后台/超大型项目仍在用;中小项目基本被替代
2. 主流状态管理横向对比
Redux
- 风格:函数式、不可变、集中式
- 优点:可预测、调试强、标准化、适合多人协作大型项目
- 缺点:繁琐、boilerplate 重
- 适用:超复杂业务、中后台系统、需要强规范的团队
MobX
- 风格:响应式、可变数据、OOP 风格
- 优点:写法简单、自动追踪依赖、更新精准
- 缺点:黑盒、调试难、可预测性弱
- 适用:追求开发速度、复杂交互客户端应用
Zustand(当前最火)
- 风格:极简、无 Context、轻量、不可变可选
- 优点:
- 代码极少,几行一个全局状态
- 不依赖 Context,无嵌套地狱
- 细粒度选取状态,不会无脑重渲染
- 支持异步、中间件、持久化
- 缺点:生态不如 Redux 丰富
- 适用:绝大多数现代 React 项目首选
Jotai
- 风格:原子化状态(Atomic)
- 优点:
- 状态以原子为单位,天然细粒度订阅
- 不会出现“改一个状态全组件刷新”
- 组合原子非常灵活
- 缺点:心智模型稍高
- 适用:追求极致性能、频繁更新的全局状态
Recoil
- 风格:Facebook 官方、原子化
- 类似 Jotai,但更重、API 更多
- 现在使用率不如 Jotai/Zustand
3. 一张表速记对比
| 方案 | 核心特点 | 可变/不可变 | 渲染机制 | 上手难度 | 主流地位 |
|---|---|---|---|---|---|
| Redux | 单向数据流、可预测 | 不可变 | 手动选择+广播 | 高 | 传统巨头 |
| MobX | 响应式、自动追踪 | 可变 | 精准更新 | 中 | 老牌强势 |
| Zustand | 极简、无Context | 可选 | 手动精准选取 | 极低 | 当下顶流 |
| Jotai | 原子化、细粒度 | 不可变 | 原子级精准 | 中 | 高性能首选 |
4. 选型建议
- 中小型项目 → Zustand 无脑选
- 极致性能、频繁更新 → Jotai
- 超大型复杂团队、强规范 → Redux Toolkit
- 快速开发、不在意调试黑盒 → MobX
5. 总结
- Redux 是规范与可预测
- MobX 是便捷与响应式
- Zustand 是简单与实用
- Jotai 是精准与性能
目前前端趋势:轻量、原子化、脱离 Context、减少重渲染,Zustand + Jotai 基本占据主流。
Zustand 简单示例
无 Context、无 Provider、无繁琐模板。
1. 安装
npm install zustand
# or
yarn add zustand
2. 定义 store
// store/useCountStore.js
import { create } from 'zustand';
// 一句话创建全局状态
const useCountStore = create((set) => ({
count: 0,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
// 异步也完全无压力
asyncInc: async () => {
await new Promise(resolve => setTimeout(resolve, 500));
set((state) => ({ count: state.count + 1 }));
},
}));
export default useCountStore;
3. 组件里使用
import useCountStore from './store/useCountStore';
function Counter() {
// 只订阅需要的状态,精准不重渲染
const count = useCountStore((state) => state.count);
const inc = useCountStore((state) => state.inc);
const asyncInc = useCountStore((state) => state.asyncInc);
return (
<div>
<h2>count: {count}</h2>
<button onClick={inc}>+1</button>
<button onClick={asyncInc}>异步+1</button>
</div>
);
}
export default Counter;
4. 另一个组件共享状态
import useCountStore from './store/useCountStore';
function AnotherComponent() {
// 跨组件自动同步
const count = useCountStore((state) => state.count);
return <p>另一个组件:{count}</p>;
}
对比优势
- 不用包裹 App 顶层 Provider
- 不会因为一个状态变,所有组件都重渲染
- 订阅精准:
(state) => state.count只监听这一个字段 - 同步/异步写法完全一致
- 体积超小,API 极少,3 分钟上手
进阶
持久化(刷新不丢)
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useUserStore = create(
persist(
(set) => ({
user: null,
setUser: (user) => set({ user }),
}),
{ name: 'user-storage' } // localStorage key
)
);
切片模式(大型项目拆分 store)
const useStore = create((set) => ({
...useCountStore.getState(),
...useUserStore.getState(),
}));