本节摘要:当共享半径超出 Context 的舒适区,状态库登场。本节把三家主流方案放进同一场景各写一遍,从心智模型、订阅粒度、样板成本、调试体验四个角度对照,最后给出按团队规模与项目形态的选型决策表。核心结论先行:库没有高下,只有匹配——「约束换取可预测」的 Redux、「极简换取轻快」的 Zustand、「响应式换取直觉」的 MobX,各有各的土壤。
用一个比喻先建立直觉:Redux 是「中央登记处」——一切变更必须填单(action)、走流程(reducer)、盖章入库,流程繁琐但每一笔都有据可查,时间旅行调试就是这套登记制度的红利;Zustand 是「自助货架」——想拿哪个状态直接订阅哪个,没有中间商,代价是纪律全靠自觉;MobX 是「智能仓储」——你像操作普通对象一样改数据,它自动追踪谁依赖了什么、只通知真正相关的订阅者,魔法多但黑盒也多。三种哲学没有对错,匹配团队气质与项目形态才是正解。
三段代码把差异落到实处。先看 Zustand 的极简:创建仓库就是一个函数,组件里一个钩子加选择器搞定,没有 Provider、没有样板:
import { create } from 'zustand'; // 仓库定义:状态与变更动作同居一体 const usePlayerStore = create(set => ({ currentTrack: null, isPlaying: false, play: track => set({ currentTrack: track, isPlaying: true }), pause: () => set({ isPlaying: false }), })); // 组件内:选择器订阅,只在本切片变化时重渲染 function PlayButton() { const isPlaying = usePlayerStore(s => s.isPlaying); const pause = usePlayerStore(s => s.pause); return <Text onPress={pause}>{isPlaying ? '暂停' : '未播放'}</Text>; }
再看 Redux 工具链时代的写法——切片把动作与迁移函数合并声明,样板比旧版 Redux 少了一半,但「填单走流程」的骨架未变:
import { createSlice, configureStore } from '@reduxjs/toolkit'; // 切片:动作类型由框架生成,迁移逻辑仍是纯函数 const playerSlice = createSlice({ name: 'player', initialState: { currentTrack: null, isPlaying: false }, reducers: { play(state, action) { state.currentTrack = action.payload; state.isPlaying = true; }, pause(state) { state.isPlaying = false; }, }, }); const store = configureStore({ reducer: playerSlice.reducer }); // 组件侧经 Provider 注入后用 useSelector 订阅,dispatch 提交变更
MobX 的写法在这里省略整段,只点出气质差异:声明一个可观察的播放器类,组件里直接读写它的字段,改了就是改了,订阅与通知由框架的依赖追踪自动完成——代码量最少,心智最接近「操作普通对象」,代价是数据流不再一目了然,出问题要靠工具看依赖图。三家放在一起的完整对照如下。

浓缩成一张会上能直接用的表:
| 决策维度 | Redux | Zustand | MobX |
|---|---|---|---|
| 团队规模 | 大团队强约束 | 小队高自治 | 中队需响应式经验 |
| 样板成本 | 高(工具链已减半) | 极低 | 低 |
| 调试体验 | 时间旅行最强 | 轻量插件 | 依赖图工具 |
| 学习曲线 | 概念最多 | 几乎没有 | 响应式范式 |
| 典型风险 | 过度设计 | 纪律松散 | 黑盒排错 |
选型定了,落地还有几处容易翻车的细节。细节一:订阅粒度要显式。 无论哪家库,「订阅整仓」与「订阅字段」的重渲染差距是数量级的。Zustand 用选择器函数、Redux 用精确的 useSelector、MobX 靠观察粒度——共同点是组件只声明自己关心的最小切片。判断写对了没有,用渲染计数验证一次最可靠。细节二:持久化的接缝。 全局状态通常要接 4.3 的持久化(登录态、设置项),接缝处要定两件事:哪些切片持久化(不是全部,瞬时 UI 状态落盘是浪费)、 hydration 的时机(恢复完成前显示加载态,避免「闪一下旧值」)。细节三:调试工具先装再写。 日志中间件、时间旅行、状态导出,这些工具在第一个组件接入前配好,出了问题才有现场可查——事后补装调试工具,等于事故之后才安装摄像头。
顺带回应一个高频纠结:「小项目要不要用状态库?」判据不是项目大小,是状态形状——如果全局共享状态不超过两三块且都是低频设置型,Context 足矣;一旦出现「多页面共同读写的高频状态」,无论项目多小都该上库,因为 Context 的连坐重渲染与项目大小无关。
背景:某工具类应用由三人小队维护,历史包袱是早期照搬大厂模板引入的 Redux,实际全局状态只有播放器与设置两块,动作单与切片文件占了状态层代码的八成。操作:先盘点真实依赖——两块状态都没有时间旅行与审计诉求;再评估迁移面——组件侧订阅写法从 useSelector 换成带选择器的钩子,约四十处机械替换;然后用灰度方式并行迁移,先迁设置模块观察两周再迁播放器。结果:状态层代码量降到原来的一半以下,新同事上手时间明显缩短,调试体验略有下降但团队无感。解读:这次迁移的成立前提是「约束买贵了」——Redux 的纪律是为大团队协作付的保费,三人小队用不上这份保额。变式:如果这家应用后来接入二十人协作或金融级审计需求,迁回 Redux 或在 Zustand 上自建约定层都是合理路径——选型是动态契约,跟着团队规模与合规要求走。
状态的水源还剩两条:网络与磁盘。下一节把异步数据的四道必答题和三类存储介质一次讲清。