redux中的middleware解析

为什么需要middleware

在没有middleware之前,Redux的工作流程是这样的

redux中的middleware解析_第1张图片

上图表达的是 redux 中一个简单的同步数据流动的场景
面对多种多样的业务需求,单纯的修改 dispatchreducer的代码显然不具有普世性,我们需要的是可以组合的,自由插拔的插件机制。 另外, reducer更关心的是数据的转化逻辑,所以 reduxmiddleware 是为了增强 dispatch 而出现的
redux中的middleware解析_第2张图片

上面这张图展示了应用 middlewareredux 处理事件的逻辑,每一个 middleware处理一个相对独立的业务需求,通过串联不同的 middleware,实现变化多样的的功能


理解middleware机制

1. 函数式编程思想设计 middleware

易串联。柯里化函数具有延迟执行的特性,通过不断柯里化形成的 middleware可以累积参数,配合组合的方式,很容易形成 pipeline 来处理数据流
共享store。在 applyMiddleware 执行过程中,store 还是旧的,但是因为闭包的存在,applyMiddleware完成后,所有的 middlewares内部拿到的 store 是最新且相同的

2. 给 middleware 分发 store
let newStore = applyMiddleware(mid1, mid2, mid3, ...)(createStore);

3. 组合串联 middlewares
dispatch = compose(...chain)(store.dispatch);

applyMiddleware 源码分析

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer)
    var dispatch = store.dispatch
    var chain = []

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    return {
      ...store,
      dispatch
    }
  }
}
  • 通过...middlewares将所有的中间件存入到middlewares数组中
  • middlewares数组通过 map 方法执行生成新的middlewares数组且每一项都传入middlewareAPI,传入middlewareAPI的目的就使得每个中间件都可以访问到store,这时候middlewares数组的每一项都变为了
function (next) {
    return function (action) {...}
}
  • compose方法将新的 middlewaresstore.dispatch结合起来,生成一个新的 dispatch 方法
    我们这里可以来模拟一下compose函数处理完的结果,假设我们这边有两个中间件A和B,则传入到composefunc为[A, B],且A、B的形式已经是(next) => (action) => {}
function A(next) {
    console.log('A...next === ', next)
    return function(action) {
        console.log('A...action')
        next(action)
    }
}
function B(next) {
    console.log('B...next === ', next)
    return function(action) {
        console.log('B...action')
        next(action)
    }
}
function compose(funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)
  const fn = (args) => rest.reduceRight((composed, f) => f(composed), last(args))
  return fn
}
var fnArr = [A, B]
var dispatch = compose(fnArr)("store.dispatch")
console.log('new dispatch === ', dispatch)

执行的结果是:

redux中的middleware解析_第3张图片

由结果可以看到中间件A的 next是指向中间件B的最内层闭包函数,而中间件B的 next则是指向原生的 dispatch,所以通过 compose执行完后,所有的中间件就通过next串联起来了

  • 返回的 store 新增了一个 dispatch方法, 这个新的 dispatch 方法是改装过的 dispatch

总结

applyMiddleware 机制的核心在于组合 compose,将不同的 middlewares 一层一层包裹到原生的 dispatch 之上,而为了方便进行 compose,需对 middleware 的设计采用柯里化 curry 的方式,达到动态产生next 方法以及保持store 的一致性。由于在 middleware中,可以像在外部一样轻松访问到 store, 因此可以利用当前 storestate 来进行条件判断,用 dispatch 方法拦截老的 action或发送新的 action


参考资料

http://www.cnblogs.com/canfoo/p/6119446.html
http://div.io/topic/1530
https://zhuanlan.zhihu.com/p/20597452
http://cn.redux.js.org/docs/advanced/Middleware.html

你可能感兴趣的:(redux中的middleware解析)