为什么需要middleware
在没有middleware之前,Redux的工作流程是这样的
上图表达的是
redux
中一个简单的同步数据流动的场景面对多种多样的业务需求,单纯的修改
dispatch
或 reducer
的代码显然不具有普世性,我们需要的是可以组合的,自由插拔的插件机制。 另外,reducer
更关心的是数据的转化逻辑,所以 redux
的 middleware
是为了增强 dispatch
而出现的上面这张图展示了应用
middleware
后redux
处理事件的逻辑,每一个 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
方法将新的middlewares
和store.dispatch
结合起来,生成一个新的dispatch
方法
我们这里可以来模拟一下compose
函数处理完的结果,假设我们这边有两个中间件A和B,则传入到compose
的func
为[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)
执行的结果是:
由结果可以看到中间件A的
next
是指向中间件B的最内层闭包函数,而中间件B的next
则是指向原生的dispatch
,所以通过compose
执行完后,所有的中间件就通过next串联起来了
- 返回的
store
新增了一个dispatch
方法, 这个新的dispatch
方法是改装过的dispatch
总结
applyMiddleware
机制的核心在于组合 compose
,将不同的 middlewares
一层一层包裹到原生的 dispatch
之上,而为了方便进行 compose
,需对 middleware
的设计采用柯里化 curry
的方式,达到动态产生next
方法以及保持store
的一致性。由于在 middleware
中,可以像在外部一样轻松访问到 store, 因此可以利用当前 store
的 state
来进行条件判断,用 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