网络日志

redux源码解析之中间件

理解中间件

简单来说,中间件就是对store 的dispatch方法进行包装扩展。它在 dispatch action 和到达 reducer 的那一刻之间提供了逻辑插入点。可以使用 Redux 中间件进行日志记录、异常监控、与异步 API 对话、路由等。具体可以见官方文档 Middleware 中间件 | Redux 中文官网

具体实现

在redux中常见的中间件写法为

function middleWare({ getState,dispatch }) {
  return next => action => {
       // xxx   
    return next(action)
   }
}

我们可以看到这里多次使用到高阶函数,看起来很复杂,下面我们来对他进行逐步的解析。为了方便标记,我这里将箭头函数,改成函数申明的形式

function middleWare1({ getState, dispatch }) {
  return function nextHandle1(next) {
    return function actionHandle1(action) {
         //xxx
      return next(action)
    }
  }
}

下面就开始讲解middleware nextHandle actionHandle的具体使用场景

applyMiddleware

首先从入口讲起,常见的使用中间件的方式如下const store = createStore(reducer, applyMiddleware([thunk, logger]));可以看出,我们是通过applyMiddleware方法将中间件方法包了一层。那么对应的applyMiddleware方法中处理中间件的流程为:

export default function applyMiddleware(
  ...middlewares: Middleware[]
) {
// 省略部分代码
  const middlewareAPI: MiddlewareAPI = {
    getState: store.getState,
    dispatch: (action, ...args) => dispatch(action, ...args)
  }
 // 调用middleware方法,并传参
  const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 核心逻辑 将函数按序组合 compose(a, b, c)(dispatch) => a(b(c(dispatch)))
  dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
  return {
    ...store,
    dispatch
  }
}
//简化compose函数实现
export default function compose(...funcs: Function[]) {
    return funcs.reduce(function(preFnA, itemFnB){
      return function (...args) {
          return preFnA(itemFnB(...args));
      }
  });
}

通过上面两行核心代码middlewares.map以及dispatch = compose(xxx),我们可以分析得到middlewares实质上是在对原有的dispatch进行改写加强。对应的我们之前写的middleWare1函数在此处被调用,那么就成了

const chain = middlewares.map(middleware => middleware(middlewareAPI))
// middlewares在调用map方法后就成了
const chain = [nextHandle1, nextHandle2,...]

dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
// chain中函数再次被compose组合,那么就成了
dispatch = actionHandle1(actionHandle2(actionHandle3(store.dispatch)))
dispatch = function actionHandle1(action){
   console.log('start1')
   const result = next(action);//此处的next函数即nextHandle2中的actionHandle2
   console.log('end1')
   return result 
}
function actionHandle2(action){
   console.log('start2')
   const result = next(action); //此处的next函数即actionHandle3
   console.log('end2')
   return result 
}
function actionHandle3(action){
   console.log('start3')
   const result = next(action); //此处的next函数即store.dispatch
   console.log('end3')
   return result 
}

实际上在middlewares调用过程中,可以用洋葱模型(面向切面编程(AOP))来描述上述代码在执行过程中会依次打印 start1=> start2 =>start3 =>end3=> end2 =>end1。最中间的那一层就是原始的store.dispatch(action),其余包裹它的中间件,都是对dispatch的加强

总结

到这里,结构就比较清晰明了了,可以用一个图来概括