前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redux middleware 源码分析

redux middleware 源码分析

作者头像
牧云云
发布2018-05-02 12:25:15
1.2K0
发布2018-05-02 12:25:15
举报
文章被收录于专栏:云瓣云瓣

原文链接

middleware 的由来

在业务中需要打印每一个 action 信息来调试,又或者希望 dispatch 或 reducer 拥有异步请求的功能。面对这些场景时,一个个修改 dispatch 或 reducer 代码有些乏力,我们需要一个可组合的、自由增减的插件机制,Redux 借鉴了 Koa 中 middleware 的思想,利用它我们可以在前端应用中便捷地实现如日志打印、异步请求等功能。

比如在项目中,进行了如下调用后,redux 就集成了 thunk 函数调用以及打印日志的功能。

代码语言:javascript
复制
import thunk from 'redux-thunk'
import logger from '../middleware/logger'
const enhancer = applyMiddleware(thunk, logger),  // 以 redux-thunk、logger 中间件为例介绍中间件的使用
const store = createStore(rootReducer, enhancer)

下面追本溯源,来分析下源码。

applyMiddleware 调用入口

代码语言:javascript
复制
export default function createStore(reducer, preloadedState, enhancer) {
  // 通过下面代码可以发现,如果 createStore 传入 2 个参数,第二个参数相当于就是 enhancer
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }
  if (typeof enhancer !== 'undefined') {
    return enhancer(createStore)(reducer, preloadedState)
  }
  ...
}

由上述 createStore 源码发现,applyMiddleware 会进行 applyMiddleware(thunk, logger)(createStore)(reducer, preloadedState) 的调用。

applyMiddleware 源码如下

代码语言:javascript
复制
export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
      getState: store.getState,                // 调用 redux 原生方法,获取状态
      dispatch: (...args) => dispatch(...args) // 调用 redux 原生 dispatch 方法
    }
    // 串行 middleware
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch // 返回加工过的 dispatch
    }
  }
}

可以发现 applyMiddleware 的作用其实就是返回加工过的 dispatch,下面会着重分析 middlewares 是如何串行起来的以及 dispatch 是如何被加工的。

串行 middleware

代码语言:javascript
复制
const middlewareAPI = {
  getState: store.getState,
  dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

观察上述代码后发现每个 middleware 都会传入参数 middlewareAPI,来看下中间件 logger 的源码 以及 redux-thunk 的源码, 发现中间件接受的第一个参数正是 ({ dispatch, getState })

代码语言:javascript
复制
// logger 源码
export default ({ dispatch, getState }) => next => action => {
  console.log(action)
  return next(action) // 经 compose 源码分析,此处 next 为 Store.dispatch
}
代码语言:javascript
复制
// redux-thunk 源码
export default ({ dispatch, getState }) => next => action => {
  if (typeof action === 'function') {
    return action(dispatch)
  }
  return next(action) // 此处 next 为 logger 中间件返回的 (action) => {} 函数
}

dispatch 是如何被加工的

接着上个小节,在 dispatch = compose(...chain)(store.dispatch) 中发现了 compose 函数,来看下 compose 的源码

代码语言:javascript
复制
export default function compose(...funcs) {
  // ...
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

compose 源码中的 funcs.reduce((a, b) => (...args) => a(b(...args))) 算是比较重要的一句,它的作用是返回组合参数后的函数,比如 compose(f, g, h) 等价于 (...args) => f(g(h(...args))),效果图如下所示,调用 this.props.dispatch() 后,会调用相应的中间件,最终会调用 redux 原生的 store.dispatch(),并且可以看到中间件调用的形式类似数据结构中的栈(先进后出)。

拿上个小节提到的 logger、redux-thunk 中间件为例,其 middleware 的内部串行调用方式如下,从而完成了 dispatch 功能的增强(支持如 this.props.dispatch(func) 的调用以及日志功能)。具体可以看 项目中的运用

代码语言:javascript
复制
action => {
  if (typeof action === 'function') {
    return action(dispatch)
  }
  return (action => {
    console.log(action)
    return store.dispatch(action)
  })(action)
}

参考文献

深入React技术栈

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-03-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • middleware 的由来
  • applyMiddleware 调用入口
  • 串行 middleware
  • dispatch 是如何被加工的
  • 参考文献
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档