原文: redux的applyMiddleware細(xì)節(jié)分析
在上篇文章中有提到中間件里有定義一個(gè)dispatch方法用來(lái)拋出異常,然后在middlewareAPI里調(diào)用的時(shí)候是攜帶參數(shù)了:
const middlewareAPI = { getState: store.getState, //獲取store里的state dispatch: (...args) => dispatch(...args) // 調(diào)用`dispatch`的時(shí)候會(huì)拋錯(cuò),如果在組合中間件之前調(diào)用,下面會(huì)說(shuō) }一開(kāi)始我以為是在調(diào)用的時(shí)候就會(huì)報(bào)錯(cuò),可是發(fā)現(xiàn)這個(gè)對(duì)象里的dispatch攜帶參數(shù),如果只是單純拋錯(cuò),完全可以不需要傳遞參數(shù),然后向下看下去才看到其中的奧妙。
到底奧妙在哪里,這段拋錯(cuò)代碼究竟是有什么作用。
先說(shuō)這段代碼的作用: 防止在對(duì)dispatch擴(kuò)展之前調(diào)用dispatch。
我們來(lái)寫個(gè)簡(jiǎn)單的middleware:
const exampleMiddle = ({getState, dispatch}) => next => action => {
console.log('exampleMiddleware action is', action)
return next(action)
}
假設(shè)我們當(dāng)前并不知道那段dispatch函數(shù)拋出錯(cuò)誤的作用,我們可以根據(jù)applyMiddleware代碼來(lái)推算出來(lái)。
我們知道,在代碼里有對(duì)dispatch重新賦值操作:
dispatch = compose(...chain)(store.dispatch)
所以我們可以知道,在這個(gè)賦值之前調(diào)用的dispatch都是會(huì)拋錯(cuò)的(就是那段單純拋出錯(cuò)誤的dispatch函數(shù))。
所以為了證明這個(gè),我們來(lái)試下:
const exampleMiddle = ({getState, dispatch}) => next => {
console.log('dispatch action', dispatch({}))
return action => {
console.log('exampleMiddleware action is', action)
return next(action)
}
}
然后你就會(huì)發(fā)現(xiàn)console里有錯(cuò)誤拋出,就是那段dispatch的throw。
然后我們?cè)?code>dispatch = compose(...chain)(store.dispatch)之后再次調(diào)用dispatch,
也就是在最終觸發(fā)action的時(shí)候再調(diào)用,發(fā)現(xiàn)是可以調(diào)用(需要加判斷是否調(diào)用過(guò),否則就會(huì)死循環(huán)。后面解釋為啥死循環(huán)。)
會(huì)不會(huì)感覺(jué)不太對(duì),為啥在閉包作用域鏈外的更改會(huì)影響到閉包內(nèi)的變量?
先來(lái)證明,閉包內(nèi)的變量不受外部改變:
let name = 'first arg'
function run(firstArg){
console.log('run params is', firstArg)
return function next(){
console.log('next params is', firstArg)
}
}
let next = run(name)
name = 'changed'
next() // next params is first arg
對(duì)吧,說(shuō)明外部的更改是不會(huì)影響到內(nèi)部的變量的。因?yàn)樽饔糜蜴溡呀?jīng)生成了。
我們?cè)賮?lái)看看另外一個(gè)情況:
let age = 99
let obj = {
old: age,
young: () => age
}
age = 18
obj.old // 99
obj.young() // 18
很明了了吧,因?yàn)槊看魏瘮?shù)調(diào)用的都是當(dāng)前作用域鏈的變量。如果對(duì)于這部分不理解,可以看這篇文章進(jìn)行深入了解。
可以發(fā)現(xiàn)我們自己寫的exampleMiddleware里,第一個(gè)參數(shù)里有傳入dispatch,然后返回的參數(shù)里攜帶了一個(gè)next參數(shù)(store.dispatch), 為什么還要傳遞dispatch, 并且dispatch也不可以在前面使用,只能在到action這步的時(shí)候才可以。所以為啥還要傳遞dispatch進(jìn)來(lái),直接傳遞了store.dispatch不是更好嗎? 這兩個(gè)不等價(jià),store.dispatch是原生的dispatch,所以可以直接返回,不會(huì)出現(xiàn)死循環(huán)。參數(shù)dispatch是組合中間件之后的dispatch,所以簡(jiǎn)單的直接調(diào)用會(huì)死循環(huán),會(huì)自己調(diào)用自己,所以需要判斷下。
總結(jié): middlewares通過(guò)compose組合這些中間件成一個(gè)鏈?zhǔn)降?code>dispatch,這樣每次dispatch都會(huì)運(yùn)行處理,可以發(fā)現(xiàn)dispatch源碼里,最后返回的是action。