状态三国演义:Redux、Zustand、Jotai 源码趣谈

245 阅读7分钟

你可能用过 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 是 “响应式哲学” 的代言人。

五、源码哲学对比

特性ReduxZustandJotai
核心结构Reducer + Store + ActioncreateStore + Hook原子 Atom + Provider
Context✅ 必须❌ 不用✅ 可选(用于依赖图)
懒加载支持❌ 无✅ 内建✅ 优雅且兼容 Suspense
写法风格函数式 + 模块化Hook 风格 + 自由哲学式声明 + 响应式
学习曲线📈 陡峭🟦 平稳📉 初学友好,高级复杂
适用场景企业级系统中小项目 + 快速原型响应式编程、组件粒度状态

六、结语:源码之下,皆有趣味

三大状态库,各有性格,各有光辉。

  • Redux 是架构控的浪漫
  • Zustand 是自由派的狂欢
  • Jotai 是响应式哲学的轻吟

源码不仅是工具的“内部运作”,更是设计哲学与取舍艺术的结晶

📚 阅读源码的姿势:

  • 带着问题去看
  • 从用户代码出发,逆推到内部实现
  • 找到每一个 “aha!” 的瞬间

状态管理没有银弹,但有哲学。

Redux 教我们组织和规范,Zustand 给我们灵活与自由,而 Jotai 提醒我们“原子化”思维的美妙。

如果你想写一个状态库,不妨多看看他们的源码。你会发现,每一行“简单”的代码背后,其实都藏着深思熟虑的设计决策。

💬 最后:你更喜欢哪一个状态库?或者你有没有看过哪个库的源码印象最深?欢迎留言讨论!

如果这篇文章对你有启发,一键三连 + 收藏 是我继续产出源码趣读内容的最大动力❤️