你可能用过 Redux,也可能听说过 Zustand 和 Jotai。它们风格各异,但你有没有想过,它们的“性格”其实就藏在源码里?
本文将用一种轻松趣味的方式,带你深入三大主流 React 状态库的源码核心,从 Redux 的严谨,到 Zustand 的野性,再到 Jotai 的哲学,我们一起探究状态管理的“性格密码”。
没有枯燥的源码大段堆砌,有的是清晰逻辑、趣味比喻和可视化解析。
👉 准备好了吗?来一场状态管理的三国探秘之旅吧!
状态管理江湖,群雄并起。Redux 持剑而立,Zustand 嘶吼奔腾,Jotai 端坐冥思。今日我潜入三大门派,直探源码秘笈,与你共赏这场状态三国的传奇。
一、引言:状态的烦恼
曾几何时,React 的 useState 用得正香,组件树一复杂,状态就满天飞。
- 数据共享:prop drilling 痛苦无比
- 组件通信:context 一层层包
- 异步状态:还得加 middleware?
于是江湖上诞生了三大门派:
- Redux:长者风范,纪律严明,招式繁多
- Zustand:野性十足,灵活简单,喜欢钩子
- Jotai:哲学派别,万物皆原子,依赖追踪
它们不仅风格各异,源码中更藏着各自的“性格密码” ,接下来我们就一探究竟!
二、Redux:老派骑士的战斗美学
Redux 是 React 状态管理领域的“武当派”。讲究纪律、数据单向流、不可变,是许多项目的启蒙之选。
🌟 核心机制回顾
createStore:建功立业的入口函数dispatch(action):发起一次战斗reducer(state, action):决定如何改变世界subscribe(listener):监听战况
🔍 源码核心拆解:createStore
function createStore(reducer, preloadedState, enhancer) {
let currentState = preloadedState
let currentListeners = []
function getState() {
return currentState
}
function dispatch(action) {
currentState = reducer(currentState, action)
currentListeners.forEach(listener => listener())
return action
}
function subscribe(listener) {
currentListeners.push(listener)
return () => {
const index = currentListeners.indexOf(listener)
currentListeners.splice(index, 1)
}
}
// 派发一次初始化 action
dispatch({ type: '@@redux/INIT' })
return { getState, dispatch, subscribe }
}
🔧 重点解析:
reducer是“状态的唯一改造者”dispatch就是发号施令,触发 state 更新subscribe是监听器列表,纯数组,简单高效
🧠 源码的美感:
Redux 核心逻辑不到 100 行,却构建出一个完整的状态系统。这种结构就像一个“微型操作系统”,你能感受到作者那种“我不搞花活,代码要讲道理”的精神。
🍄 applyMiddleware:洋葱中传秘籍
Redux 支持中间件,通过 applyMiddleware(...middlewares) 实现异步、日志等功能。来看它的实现核心:
function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState)
let dispatch = store.dispatch
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action),
}
const chain = middlewares.map((middleware) => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return { ...store, dispatch }
}
}
关键词:compose、闭包、递归调度
🌰 举个栗子:
const logger = ({ getState }) => next => action => {
console.log('before:', getState())
const result = next(action)
console.log('after:', getState())
return result
}
是不是像一颗洋葱?一层层包裹住 dispatch,每层都可以动手脚,堪称函数式编程的艺术品!
🧝 Redux 小结:老骑士的优雅与疲惫
Redux 就像一位着甲持剑的老骑士:
- 🏛️ 优点:稳定、可预测、开发工具强大
- 🐌 缺点:样板多、入门曲线陡、写起来有点“重”
虽然它已经不再风靡,但它的“源码结构之美”依然值得每一位工程师学习。
三、Zustand:状态管理的灵兽馆
Zustand(德语中的“状态”)是一个由 Poimandres 团队打造的轻量状态库,特点是:
- 不用 Provider,不用 Context
- 核心 API 一个:
create - 写起来像“驯一只动物”:你设定它,它忠实响应
🧠 核心机制回顾
Zustand 的使用方式大概长这样:
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
你在组件里只要这样用:
const count = useStore((state) => state.count)
没有 context、没有 reducer、甚至没有 action 类型 —— 是不是有点“反 Redux”的味道?
🔍 源码核心拆解:createStore
Zustand 本质是封装了一个状态容器,代码核心如下:
export const createStore = (initializer) => {
let state
const listeners = new Set()
const setState = (partial, replace) => {
const nextState = typeof partial === 'function' ? partial(state) : partial
if (!Object.is(nextState, state)) {
const previousState = state
state = replace ? nextState : { ...state, ...nextState }
listeners.forEach((listener) => listener(state, previousState))
}
}
const getState = () => state
const subscribe = (listener) => {
listeners.add(listener)
return () => listeners.delete(listener)
}
const api = { setState, getState, subscribe }
state = initializer(setState, getState, api)
return api
}
🧪 设计亮点:
- 使用闭包保存
state与listeners,不用 React context - 通过
subscribe精准监听,性能比 context 更稳 setState支持函数式更新,支持合并与替换
🐻 Zustand 像什么?
它像一只你亲手养大的灵兽:
- 你教它:“当我点按钮就 count++”
- 它记住后,每次都忠诚响应
- 而且不吵不闹,不加 Provider,不抱怨 Props
一句话总结:
Redux 是写代码,Zustand 是养宠物。
🔍 Selector 精准监听的实现
Zustand 的 useStore(selector) 并不会导致全组件更新,因为它用了 shallow equality + 订阅机制:
useSyncExternalStore(
store.subscribe,
() => selector(store.getState()),
)
React 的 useSyncExternalStore 是为了这种外部状态订阅而生,Zustand 在这里用得恰到好处。
四、Jotai:状态即原子,原子即宇宙
Jotai(日语中意为“原子”)是 Recoil 的轻量替代者,由著名的 Zustand 团队出品。它提出了一个哲学式概念:
组件 = 原子状态的观察者
每个状态都是独立原子,互不干扰,天然响应式。
🧠 核心机制回顾
Jotai 的使用方式像这样:
const countAtom = atom(0)
function Counter() {
const [count, setCount] = useAtom(countAtom)
return <button onClick={() => setCount((c) => c + 1)}>{count}</button>
}
就像 Vue 的 ref,一切都是“声明原子 + 使用原子”。
🔍 源码核心拆解:createAtom + read/write
Jotai 的原子其实是一个描述对象(不是值本身):
const countAtom = {
read: () => 0,
write: (get, set, update) => ...
}
再结合 useAtom 和 Provider 构建状态依赖图。核心代码(简化后):
const atomState = new WeakMap()
function readAtom(atom) {
const value = atom.read(getter)
atomState.set(atom, { value })
return value
}
function writeAtom(atom, update) {
if (atom.write) {
atom.write(getter, setter, update)
}
}
Jotai 是一个运行时构建依赖关系图的库,当你读取一个 atom 时,它就追踪了谁依赖谁。
🧮 Suspense & 异步 atom
Jotai 支持 Suspense,异步 atom 只需:
const userAtom = atom(async () => {
const res = await fetch('/api/user')
return res.json()
})
一旦调用 useAtom(userAtom),Suspense 自动生效。
这要归功于 Jotai 在内部构建了一张“Promise 状态图”,并且用异步缓存做了懒加载处理。
🧘 Jotai 像什么?
Jotai 更像一位禅宗大师:
- 不喧哗,不绑定 UI 框架
- 所有状态皆可组合,读写可分离
- 当你需要异步,甚至 Suspense,它也心如止水
一句话总结:
Jotai 是 “响应式哲学” 的代言人。
五、源码哲学对比
| 特性 | Redux | Zustand | Jotai |
|---|---|---|---|
| 核心结构 | Reducer + Store + Action | createStore + Hook | 原子 Atom + Provider |
| Context | ✅ 必须 | ❌ 不用 | ✅ 可选(用于依赖图) |
| 懒加载支持 | ❌ 无 | ✅ 内建 | ✅ 优雅且兼容 Suspense |
| 写法风格 | 函数式 + 模块化 | Hook 风格 + 自由 | 哲学式声明 + 响应式 |
| 学习曲线 | 📈 陡峭 | 🟦 平稳 | 📉 初学友好,高级复杂 |
| 适用场景 | 企业级系统 | 中小项目 + 快速原型 | 响应式编程、组件粒度状态 |
六、结语:源码之下,皆有趣味
三大状态库,各有性格,各有光辉。
- Redux 是架构控的浪漫
- Zustand 是自由派的狂欢
- Jotai 是响应式哲学的轻吟
源码不仅是工具的“内部运作”,更是设计哲学与取舍艺术的结晶。
📚 阅读源码的姿势:
- 带着问题去看
- 从用户代码出发,逆推到内部实现
- 找到每一个 “aha!” 的瞬间
状态管理没有银弹,但有哲学。
Redux 教我们组织和规范,Zustand 给我们灵活与自由,而 Jotai 提醒我们“原子化”思维的美妙。
如果你想写一个状态库,不妨多看看他们的源码。你会发现,每一行“简单”的代码背后,其实都藏着深思熟虑的设计决策。
💬 最后:你更喜欢哪一个状态库?或者你有没有看过哪个库的源码印象最深?欢迎留言讨论!
如果这篇文章对你有启发,一键三连 + 收藏 是我继续产出源码趣读内容的最大动力❤️