在學習了redux過程中,了解到中間件這個名詞,但是我看了十遍,也完全就是懵逼的狀態。于是又重復敲了幾次代碼也不能掌握這個東西到底是什么?看官網文檔,那么些專業名詞,讓人半天摸不著頭腦。我們從流程圖中知道,react組件在執行過程中,特別是在中間插入各種奇怪的需求的時候,不可能每每都是改動一下代碼邏輯,而是用一種方便插拔的方式添加進去。但是這個過程說得簡單,理解也容易,問題到底是怎么實現的呢?本人這樣的半吊子水平,要理解這么高深的東西,真是太困難了。反復地摸索過程中,感覺摸到一些門徑,于是斗膽做一下我的解讀,如有不對,歡迎斧正!
前言
在學習appleyMiddleware中間件之前,必須要有一些知識儲備。列出一下:
- react
- redux或者看github上的教程redux-tutorial-cn
源碼分析
applyMiddleware源碼
import compose from './compose'
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
}
}
}
createStore
第一次執行applyMiddleware增加中間件,使用閉包保存中間件,然后返回一個函數(一開始我很奇怪為什么參數是createStore??),在弄明白applyMiddleware之前,得先來看他是如何被調用的,那就得先從createStore開始看。
摘了核心的createStore
的源碼如下:
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
...
}
分析源碼可以發現其中有一段這樣的代碼:
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
翻譯一下這個參數enhancer
英文意思為:“增強器”。
我反復看這代碼,越看越吃驚,天啊!作者寫的這段代碼太讓人驚嘆了!我從未見過如此風騷的代碼!
enhancer
我們不妨再簡化一下這個createStore
源碼:
function createStore(reducer,preloadedState,enhancer){
...
return enhancer(createStore)(reducer,preloadedState)
...
}
我們知道,其實enhancer === applyMiddleware
,這樣子,我們再將enhancer
換為applyMiddleware
這時變成這樣子:
function createStore(reducer,preloadedState,enhancer){
...
return applyMiddleware(createStore)(reducer,preloadedState)
...
}
這時,我們可以將里面的返回代碼拿出來,得出這樣的:
applyMiddleware(createStore)(reducer,preloadedState) -> <enhancer>
我們可以想一下,執行這段代碼會返回什么呢?暫時先不用管,我們假設返回結果為<enhancer>。
從函數定義上,執行到此地,就被返回了,也就是到函數到此結束,下面的所有代碼都不會執行了。那createStore函數,到這里,就交給了applyMiddleware去處理了。
applyMiddleware
我們再回到applyMiddleware的源碼上來。看到,定義的applayMiddlware為(簡化之后):
function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
// ...
}
}
然后我們不妨一步一步調用此函數:
// step1
const step1 = applyMiddleware(...middlewares) //返回 function(createStore){}
// step2
const step2 = step1(createStore) //返回 function(reducer,preloadedState,enhancer){}
// step2的返回值即為
const step3 = function(reducer,preloadedState,enhancer){}
// 對比createStore的定義
function createStore(reducer,preloadedState,enhancer){}
我們在此可以驚奇地發現,原來,createStore函數居然可以通過applyMiddleware返回的!!!那在此,可以得出,createStore(reducer,preloadedState,enhancer)執行之后,如果enhancer未傳,那么就是普通的createStore了,如果傳了,那實際上,createStore已經被enhancer接管了,然后相當于再返回一個普通的createStore而已。這才是其中的精妙之處!
我們再來看一下我們平時調用createStore時的方式是這樣的:
createStore(
rootReducers, //reducer
preloadedState,
applyMiddleware( //enhancer
thunkMiddleware,
createLogger
)
)
可以將里面的applyMiddleware
替換為<enhancer>
,然后與上面的對比:
createStore(reducer,preloadedState,<enhancer>)
// 實際上,enhancer傳了參,那么返回的結果實際上也是一個普通的 createStore
在第一次調用createStore的時候,createStore先判斷是否有middlewares(enhancer)的加入,如果有,就不執行createStore后面的操作,return出去執行enhancer()。這里換一種說法:
執行createStore的時候,只要傳了中間件applyMiddleware這樣的合法參數,那么,就相當于createStore被改寫了,實際返回時,也是一個createStore方法,然后執行之后,與普通的是一樣的,而且中間可以隨意添加移除各種需求的邏輯組件。此種實現方法被冠以一個名詞:柯里化(Currying),就是將多參變成單參的函數,也就是通過函數鏈的方式進行返回以達到單參函數。這就是applyMiddleware中間件的核心價值!
** 注意:執行了enhancer(createStore)后,只傳入兩個參數(reducer,preloadedState),第三個參數 enhancer為undefined **
store
執行enhancer就要回過頭看applyMiddleware源碼。
實際上執行enhancer,返回就是我們要的createStore函數!
function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
// ...
return {
...store,
dispatch
}
}
}
//執行之后,返回值為createStore函數:
function(reducer, preloadedState, enhancer){}
由于沒有第三個參數enhancer,所以這才是真正執行createStore(),返回一個沒有 middleware的store。
我們可以看一下applyMiddleware里面有一個語句:
...
var store = createStore(reducer, preloadedState, enhancer)
...
在這里,可以看出,在內部,也執行了createStore函數的調用,也就是說,createStore將實現移交給了applyMiddleware之后,在applyMiddleware內部同樣會生成普通的store對象的。同樣,如果這里的enhancer如果存在,繼續循環原先的步驟。
middleware
我們繼續看內部的源碼為:
...
var dispatch = store.dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
...
定義了一個chain數組,存放每一個被處理過的middleware。
代碼可以這樣解釋:首先為每一個middleware以{getState,dispatch}
為參數執行一遍,其實是為了給middleware一個原生的{getState,dispatch}
兩個方法的指針,以便在middleware中調用。
而上面的applyMiddleware的參數中就有兩個參數:thunkMiddleware,createLogger,他們都是middleware。他們在傳入applyMiddleware的過程中,都被包裝過一次,并且存放在chain數組中。
請看一個簡單的middleware:
const logger = ({getState,dispatch}) => next => action {
console.log('dispatching',action)
let result = next(action)
console.log('next state',getState())
return result
}
調用后返回的chain是一個以next為參數的函數數組:
chain = [logger].map(middleware => middleware({
getState:store.getState,
dispatch:(action) => dispatch(action)
}))
compose
繼續看代碼,有一個這樣的語句:
...
dispatch = compose(...chain)(store.dispatch)
...
dispatch被compose包裝之后,重新賦值給自身。但這段語句看得莫名莫妙。這是什么鬼意思?干嘛用的?
這里,不妨來看一下compose源碼
export default 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)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
其中一段為:
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
看到這段,更加頭疼。。。反正看來看去。大體意思是這樣:compose
可以接受一組函數參數,從右到左來組合多個函數,然后返回一個組合函數。
也就是說,compose接收chain這個數組,然后用reduceRight函數進行組合,最終合成了一個新的函數,只能這么理解了。
dispatch
到這里,也就是說,dispatch已經被compose重新組裝過一次,在最后,再被組裝成一個新的store返回。
...
return {
...store,
dispatch
}
結論
middleware內部的dispatch是原生的沒有middleware時的dispatch,
每一個middleware都帶有原生的getState,dispatch和next(下一個middleware),所以我可以在middleware中不調用next,而直接調用dispatch,就跳過了后面的middleware了。
applyMiddleware中間件,其實就是將createStore接管了,然后在最終返回一個store對象。每一個中間件都是做這樣的一件事情,這樣,就可以源源不斷地往里面添加需求或者移出需求,而不必修改流程代碼上的任何邏輯。僅此而已!