記得之前第一次看redux源碼的時(shí)候是很懵逼的,尤其是看到applyMiddleware函數(shù)的時(shí)候,更是懵逼。當(dāng)然那也是半年前的事情了,前幾天把redux源碼看了下,并且實(shí)現(xiàn)了個(gè)簡(jiǎn)單的redux功能。但是沒有實(shí)現(xiàn)中間件。今天突然又想看看redux的中間件,就看了起來(lái)。
記得半年之前是從函數(shù)聲明的下一行就開始看不懂了。。。然后前段時(shí)間,看了下柯里化函數(shù),加深了高階函數(shù)的印象,所以今天打算把中間件的源碼給擼一下。
我們來(lái)看看函數(shù)聲明的下一行,也就是源碼第二行開始看:
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
}
}
}
從上面我們可以看到中間件返回了函數(shù),返回第一個(gè)函數(shù)是攜帶createStore參數(shù)的,這個(gè)是啥?從名字上我們就可以知道,就是createStore。不過為了證明,我們還是得從源碼上來(lái)看。
還記得是怎么調(diào)用的中間件的吧,大致如下:
const store = createStore(
reducer,
applyMiddleware(...middlewares)
);
可以看到中間件是在createStore參數(shù)里調(diào)用的(在參數(shù)里運(yùn)行函數(shù),導(dǎo)致傳遞給createStore的是中間件運(yùn)行后返回的結(jié)果,從上面的中間件源碼可以知道,返回的就是攜帶createStore參數(shù)的函數(shù)),現(xiàn)在我們可以進(jìn)createStore函數(shù)里看看他是怎么處理中間件返回的函數(shù)的。
redux的主要實(shí)現(xiàn)都是在createStore里實(shí)現(xiàn)的,所以我們主要看createStore里處理參數(shù)的部分:
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.')
}
//other code
}
從我們調(diào)用createStore可以知道第一個(gè)參數(shù)是reducer,第二個(gè)參數(shù)就是中間件運(yùn)行之后返回的攜帶createStore參數(shù)的函數(shù)。但是在上面的這段源碼里,我們發(fā)現(xiàn)是preloadedState來(lái)接收這個(gè)攜帶createStore參數(shù)的函數(shù),感覺不是很多,命名的'不好'。先繼續(xù)往下看,wow, 是一個(gè)判斷,他會(huì)判斷preloadedState是不是一個(gè)函數(shù),第三個(gè)參數(shù)enhancer是不是未定義;如果preloadedState是函數(shù),enhancer是未定義,那么就會(huì)把preloadedState賦值給enhancer,并且設(shè)置preloadedState是未定義。 這樣就沒有問題了,在這里,相當(dāng)于第三個(gè)參數(shù)enhancer接收了攜帶createStore參數(shù)的函數(shù)。
然后第二個(gè)判斷:
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
他會(huì)去運(yùn)行這個(gè)enhancer。這個(gè)enhancer是什么?就是我們說(shuō)的攜帶createStore的函數(shù)。
有意思的是,這個(gè)enhancer直接在這里運(yùn)行了,并且采用了createStore作為參數(shù)(這個(gè)createStore就是函數(shù)呀)。 我們?cè)賮?lái)看看enhancer(createStore)返回的是啥:
return function (...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
}
}
有意思,返回的是帶有多個(gè)參數(shù)的函數(shù)。
上面的代碼相當(dāng)于:
enhancer(createStore) ~= function(...args) => function(reducer, preloadedState)
可以看到,上面的(...args)就是相當(dāng)于(reducer, preloadedState)。
那么我們?cè)賮?lái)看看上面的function(...args), 額, 直接在第一行就再次調(diào)用創(chuàng)建store,這樣不會(huì)陷入無(wú)限循環(huán)嗎?不會(huì),因?yàn)橛袇?shù)判斷,在createStore的原方法里不會(huì)再執(zhí)行enhancer; 所以我們可以發(fā)現(xiàn),在有中間件的時(shí)候,真正的執(zhí)行createStore是在中間件里去執(zhí)行的,并且攜帶的參數(shù)是reducer, preloadedState。
所以上面第一行創(chuàng)建了個(gè)store對(duì)象,他返回的屬性有:
{
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
然后新建了個(gè)指向dispatch變量的匿名函數(shù),這個(gè)函數(shù)在調(diào)用的時(shí)候拋出異常告訴你不可以在構(gòu)造中間件時(shí)調(diào)用dispatch。
Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch.
接下來(lái)會(huì)創(chuàng)建一個(gè)middlewareAPI對(duì)象:
const middlewareAPI = {
getState: store.getState, //獲取store里的state
dispatch: (...args) => dispatch(...args) // 調(diào)用`dispatch`的時(shí)候會(huì)拋錯(cuò),如果在組合中間件之前調(diào)用,下面會(huì)說(shuō)
}
一開始我以為是在調(diào)用的時(shí)候就會(huì)報(bào)錯(cuò),可是發(fā)現(xiàn)這個(gè)對(duì)象里的dispatch攜帶參數(shù),如果只是單純拋錯(cuò),完全可以不需要傳遞參數(shù),然后向下看下去才看到其中的奧妙。
然后就是對(duì)中間件集合middlewares(數(shù)組)進(jìn)行操作:
const chain = middlewares.map(middleware => middleware(middlewareAPI)) //返回了新的集合,對(duì)應(yīng)的每個(gè)中間件調(diào)用的結(jié)果
然后就是組合這些中間件了, 這里對(duì)高階函數(shù)不熟的,可以看下柯里化函數(shù)和函數(shù)組合::
// china是上面返回的中間件的結(jié)果
dispatch = compose(...chain)(store.dispatch)
可以看到這個(gè)代碼,組合了中間件, 使用compose這個(gè)高階函數(shù)來(lái)處理的。我們看下這個(gè)高階函數(shù):
export default 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)))
}
上面的代碼比較有意思:
- 第一個(gè)判斷
如果沒有中間件作為參數(shù)傳遞,那么直接返回一個(gè)函數(shù),接收對(duì)應(yīng)的參數(shù)并且返回這個(gè)參數(shù)。 - 第二個(gè)判斷
如果如果這個(gè)中間件參數(shù)只有一個(gè),那么直接返回這個(gè)中間件函數(shù) - 最后一步
那就是多個(gè)中間件傳遞進(jìn)來(lái)的時(shí)候,他會(huì)借用reduce方法組合(這個(gè)放在后面), 會(huì)有個(gè)...args參數(shù),就是(store.dispatch),等下回說(shuō)到。
可能你對(duì)reduce不是很熟,可以簡(jiǎn)單的看下他干了什么事:
['1', 2, 3, 'n'].reduce((a, b) => console.log('a is',a , 'b is', b)) // 這樣你就會(huì)發(fā)現(xiàn)這個(gè)方法在這里的作用
其實(shí)從注釋里也可以知道:
* @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))).
把這些中間件都執(zhí)行到dispatch.
再回到上面看compose的返回:
return funcs.reduce((a, b) => (...args) => a(b(...args)))
我們?cè)倏纯粗虚g件調(diào)用compose的地方:
dispatch = compose(...chain)(store.dispatch)
從這個(gè)地方再配合看compose(...chain) => result的這個(gè)result.
- 第一個(gè)判斷
返回的是(arg) => arg就是相當(dāng)于result(arg) => arg, 果然,直接返回這個(gè)store.dispatch - 第二個(gè)判斷
返回的是唯一的一個(gè)中間件result. 然后中間件直接調(diào)用store.dispatch作為參數(shù)。 - 最后一個(gè)
這個(gè)返回的是一個(gè)函數(shù),看起來(lái)像這樣:
(...args) => a(b(...args))
這樣就相當(dāng)于result(args) => a(b(...args)),這樣就保證每個(gè)中間件都會(huì)用到dispatch,并且最終返回這個(gè)被擴(kuò)展過的dispatch.
然后可以看到中間件函數(shù)返回了對(duì)象:
{
...store,
dispatch
}
這個(gè)dispatch就是被處理過的dispatch。