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,其實就至關於從新從鏈式函數的最外層的開始調用,這就進死循環了。。。