middleware翻译过来叫中间件,这是一个很抽象的名词,按个人理解的话:“中间”是个形容词,“件”应该是一个名词。那么咱们重点关注中间这个词,中间,是在上面中间呢?其实就是在你执行正常业务的代码中间插入一部分其它代码,具体能够是在正常代码的执行前,也能够在正常代码执行后。其实学过Spring的童鞋应该很好理解,这个东西跟Spring的切面编程很相似。。。编程
前面说了,这个技术可让咱们在正常的业务代码先后执行一部分其它代码,这个其它代码能够包括:日志、鉴权啊等到一些公共处理代码。简单来讲,只有你想不到,没有作不到。redux
话很少说,先上代码:数组
const logger1 =store=> next=> action => { console.log('dispatching logger', action) next(action) console.log('next state logger', action) } const logger2 =store=> next=> action => { console.log('dispatching logger', action) next(action) console.log('next state logger', action) } const store = createStore( reducer, applyMiddleware([logger1,logger2]) )
咱们看到上面的代码中,首先声明logger一、logger2两个middleware(没错,这两个看起来很奇怪的变量就是middleware了。。。),而后在建立store的时候经过applyMiddleware来绑定到dispatch上去,这样每次咱们分发(dispatch)一个action的时候,两个middleware里的代码都会执行。闭包
对,就是这么简单,表面的简单,背后是大量逻辑很复杂的代码。。。app
下面先上applyMiddleware的源码:函数
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
这个函数是redux在createStore函数中调用的,因此它返回一个匿名函数,咱们只须要关心内部匿名函数的实现就行了。优化
让咱们来一步步分析短短的几行代码:this
咱们先看applyMiddleware内部匿名函数的前几行代码:spa
const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) }
第一行代码调用createStore函数建立一个store对象,这个没什么特别的,过。。。
第二行声明一个dispatch变量,指向一个箭头函数,函数直接报错,用来告诉用户我这会儿正初始化呢,你敢dispatch我就报错给你看!
第三行代码声明一个middlewareAPI的对象,里面包含两个属性:getState和dispatch。翻译
getState没什么好说的,重点是这个dispatch属性,这个属性指向一个箭头函数,函数直接执行dispatch函数(这个dispatch函数可不是store原生的dispatch,而是咱们在第二行声明的dispatch变量指向的箭头函数。
这块相对来讲比较简单,可是为了后面咱们好理解,这里来对前面的变量声明作以下优化:
const store = createStore(...args) // 不变 let temDispatch = () => { // 为了跟store.dispatch 区分,咱们把变量名称修改成temDispatch; throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, APIDispatch: (...args) => temDispatch(...args) // 一样的,为了区分,咱们这里用APIDispatch来表示属性变量 }
如上代码所示,为了同store.dispatch方法区分,咱们分别用 temDispatch和 APIDispatch这两个名称来替代原来的dispatch。
接下来咱们看下一行代码:
const chain = middlewares.map(middleware => middleware(middlewareAPI))
middlewares咱们知道是一个包含中间件的数组,经过数组的map处理后,咱们将会"执行一次"中间件函数,而后将返回值放到chain的数组中。
上面咱们说"执行一次“中间件函数,其实说法有点不太好理解,接下来咱们慢慢分析中间件:
const logger1 =store=> next=> action => { console.log('dispatching logger', action) next(action) console.log('next state logger', action) }
上面是一个最简单的中间件形式,可是仍是有点复杂,咱们能够先把这个中间件拆分红如下的样子:
const inner = action => { console.log('dispatching 333 logger', action) next(action) console.log('next state 4444 logger', action) }; const middle = next => action => { console.log('dispatching 333 logger', action) next(action) console.log('next state 4444 logger', action) }; const logger1 = store => next => action => { console.log('dispatching logger', action) next(action) console.log('next state logger', action) }
如上所示,咱们的middleware实际上是一个箭头函数,不严谨的说,这个函数能够被logger1()()()
这样被调用,由于第一次和第二次被调用都返回一个新的箭头函数,这里为了好理解,咱们把他们拆分为middle 和inner函数(通常是不能这么写的,由于内部的箭头函数还要经过闭包获取外部的变量值)。
说了这么多,其实最终能够归结为一句话,那就是咱们的chain数组里放的都是middle函数,也就是chain是一个middle函数的集合,这点很重要,咱们后面还会说到这个。
咱们继续看下一行代码:
dispatch = compose(...chain)(store.dispatch) 等价于 temDispatch = compose(...chain)(store.dispatch)
这行代码看着很简短,其实很难理解,咱们一步步来看。
咱们首先来看 compose(...chain)这行代码。如下是compose代码的实现:
function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
关键代码funcs.reduce((a, b) => (...args) => a(b(...args)))
, 代入compose(...chain),chain咱们上面说到,是一个middle函数的数组,而后通过reduce处理,这里比较麻烦,咱们一点点来解释:
(a, b) => (...args) => a(b(...args))
上面就是reduce函数的回调,
(...args) => a(b(...args)
这是回调的返回值,也是一个箭头函数,咱们把它命名为reduceMiddleFunc;
a变量为上次回调的返回值(不出意外的话,就是一个箭头函数,要么是chain数组的第一个值,也就是一个middle函数,要么就是上次回调的返回值,就是一个reduceMiddleFunc函数 ),
b变量为当前循环的值,也就是一个middle函数。
这样可能不太好理解,举个例子吧,假如说原来的chain数组的值为[middle1,middle2,middle3,middle4]。那么compose(...chain)以后,咱们获得(...args) => middle1(middle2(middle3(middle4(...args))))这样一个箭头函数。咱们把它命名为 chainFunc.
原来的代码是:
dispatch = compose(...chain)(store.dispatch) 等价于 temDispatch = compose(...chain)(store.dispatch)
通过咱们上面的分析后,咱们获得如下代码:
const chainFunc = (...args) => middle1(middle2(middle3(middle4(...args)))); temDispatch = chainFunc(store.dispatch)
接下来咱们来看 chainFunc(store.dispatch),也就是咱们要执行这个链式函数了,以下:
const chainFunc = (...args) => middle1(middle2(middle3(middle4(...args)))); temDispatch = chainFunc(store.dispatch) // 至关于下面一行 temDispatch = middle1(middle2(middle3(middle4(store.dispatch))))
【注意】:此处的store.dispatch是调用createStore建立的元素store的 dispatch方法,后面咱们会覆盖原生的dispatch,因此这里须要注意下。
咱们来看applyMiddleware的最后一行代码,
return { ...store, dispatch// 也就是temDispatch }
这个实际上是createStore函数的返回值,也就是说咱们上面定义的temDispatch会覆盖掉初始的store中dispatch。
也就是说,当你调用调用store.dispatch(action)的时候,就至关因而调用 middle1(middle2(middle3(middle4(store.dispatch))))(action),只要最内部的store.dispatch才是调用真正的dispatch方法。
咱们来简化一下这个代码:
const param = middle2(middle3(middle4(store.dispatch))); store.dispatch(action) 等价于 middle1(param)(action)
还记得middle函数吗?
const middle = next => action => { console.log('dispatching 333 logger', action) next(action) console.log('next state 4444 logger', action) };
当咱们执行middle1的时候,就会把param当作next参数来执行,而后返回一个 inner函数:
这是inner函数:
const inner = action => { console.log('dispatching 333 logger', action) next(action) console.log('next state 4444 logger', action) };
那么上面的代码就能够修改成以下:
const param = middle2(middle3(middle4(store.dispatch))); store.dispatch(action) 等价于 middle1(param)(action) 等价于 inner(action)
那么在inner函数内执行next函数,其实就是执行middle2(middle3(middle4(store.dispatch)))这一套,依次类推,就比如是洋葱同样,一直执行到最内部真正的 store.dispatch方法为止。
上面我说到,在最后咱们用temDispatch这个函数覆盖了原始的 store.dispatch函数,那若是咱们是inner中经过 store.dispatch去调用会发什么状况呢?
咱们已经说过,applyMiddleware最终会覆盖原始store上的dispatch方法,改为咱们的链式调用函数,若是在inner里调用store.dispatch,其实就至关于从新从链式函数的最外层的开始调用,这就进死循环了。。。