4、Redux 核心架构源码解读(下)——中间件机制的实现与函数式魅力

102 阅读3分钟

✍️ 模拟 Redux 创始人 Dan Abramov 内心设计思维

🎯 聚焦:applyMiddleware 如何劫持 dispatch

🔍 关键词:中间件链、函数组合、dispatch 增强、洋葱模型(Onion Model)


🍩 我们为什么设计 middleware?

Redux 的原始设计只处理同步状态流,但在真实业务中我们还需要:

  • 发起异步请求(如 fetch
  • 记录日志、性能分析
  • 上报埋点、安全审计
  • 报错捕获
  • 权限拦截 / 重定向跳转

你不希望把这些“副作用逻辑”塞到 reducer 里,于是我们设计了一个**“dispatch 增强系统”**:

中间件(Middleware)本质上是一个“可插拔的 dispatch 劫持器”。


🧪 看例子:如何写一个日志中间件?

const logger = store => next => action => {
  console.log('dispatching', action)
  const result = next(action)
  console.log('next state', store.getState())
  return result
}

🔍 结构拆解:

middleware = store => next => action => {}
  • store:当前 Redux store 实例
  • next:下一个中间件或原始 dispatch
  • action:你要分发的 action

这个结构,其实就是洋葱模型函数链式封装


🧰 applyMiddleware 的源码实现(简化版)

function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)
    let dispatch = store.dispatch

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }

    const chain = middlewares.map(mw => mw(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return { ...store, dispatch }
  }
}

🧠 深度解读每一步

🧩 middlewareAPI

const middlewareAPI = {
  getState: store.getState,
  dispatch: (...args) => dispatch(...args)
}

👉 把最基本的能力(状态获取、触发 action)暴露出去给中间件用,但通过闭包劫持 dispatch,确保是增强后的 dispatch


🧰 compose:中间件链的秘密武器

function compose(...funcs) {
  return funcs.reduceRight((a, b) => (...args) => b(a(...args)))
}

这个函数做了什么?

compose(fn1, fn2, fn3)(dispatch)
// 相当于 fn1(fn2(fn3(dispatch)))

每个中间件包裹住下一个中间件,最终将 action 传给原始 dispatch。这就是**“洋葱模型”**。


🧪 举个更形象的例子:

我们注册了如下中间件:

applyMiddleware(logger1, logger2, thunk)

会得到如下执行链:

dispatch = logger1(logger2(thunk(dispatch)))

当你调用 store.dispatch(action)

  1. logger1 先执行,记录日志;
  2. 再交给 logger2 做拦截;
  3. 最后进入 thunk 判断是否为函数;
  4. 最终调用原始 reducer。

🔄 我们这样设计的原因只有一个:高度可组合性

中间件是 Redux 的“插件系统”,但我们坚持这几点:

原则解释
无依赖注入middleware 只依赖 getStatedispatch
无状态中间件函数本身是无状态函数,便于复用、测试
只包一次 dispatch所有中间件最终作用于 dispatch,逻辑单一清晰

🔥 自定义中间件场景推荐:

场景建议实现方式
打印日志loggerMiddleware
异步请求redux-thunk / redux-saga
权限验证authMiddleware
请求去重requestCacheMiddleware
报错捕获上报errorCaptureMiddleware
多语言替换i18nMiddleware

🪢 总结:Redux 中间件的真正威力

中间件是 Redux 最被模仿的部分(如 Express/Koa 的中间件链),其威力来源于三点:

  1. 函数式组合:易读、可测试、抽象度高;
  2. 扩展性强:可插拔、无副作用污染;
  3. 执行链可控:你可以控制 action 的完整生命周期。

它将“架构的中间环节”变成了“第一等公民”。


⏭️ 下一篇预告

我们即将踏入 UI 层的 Redux 世界,下一篇将深入剖析:React-Redux 是如何用 Context + 高阶组件连接 UI 与 Redux 的?

你会看到 connect(mapStateToProps) 背后究竟做了哪些性能优化与底层 magic。