網(wǎng)上的Redux中間件原理解釋多有疏漏百匆,譬如我在多篇blog上看到Redux中間件解釋以及Redux中間件深入淺出(譯文)前文解釋的時(shí)候提到了chain潘拨,這其實(shí)很不準(zhǔn)確非迹,因?yàn)樵赼pplyMiddleWare內(nèi)部不是按照Chainable call的邏輯實(shí)現(xiàn)的惩歉,很容易讓人誤會為middleware1.middleware2...這種錯誤的邏輯抡谐,下文會根據(jù)源碼進(jìn)行更正裁奇。后者,雖然調(diào)用邏輯將的大致對了麦撵,但是關(guān)于curry function不恰當(dāng)?shù)囊胝娴氖谴蟠蟮恼`導(dǎo)刽肠,因?yàn)榘凑誧urry的邏輯,調(diào)用順序和實(shí)際源碼中的完全是兩個(gè)不同方向免胃。
上面提到的緣起就是我更正的點(diǎn)和踩過的坑音五。關(guān)于middleware內(nèi)部實(shí)現(xiàn)的fn組合和調(diào)用順序的闡述,我湊巧今天早上看到一篇知乎專欄文章Redux middleware 詳解羔沙,大家可以看躺涝,這位兄弟解釋的過程和我debug得到的結(jié)果是一致的,我也不再贅述扼雏。下面坚嗜,講一講,很怪的一個(gè)約定呢蛤,為什么middleware都得習(xí)慣性最后
return next(action)
以及為何applyMiddleWare內(nèi)部必須
dispatch = compose(...chain)(store.dispatch)
這樣才能連接action 和 reducer
Q1
針對末尾的return惶傻,我只能說這真的只是習(xí)慣,因?yàn)槠湔希憧梢栽诠俜降睦觬eal-world中修改api.js和thunk內(nèi)部的實(shí)現(xiàn)银室,去掉最后的return.這是可以的,為什么呢励翼?原因和Q2有重要關(guān)系蜈敢。因?yàn)樵赒2的最后enhancer就是通過store.dispatch講前面處理過的action分發(fā)給了reducer。但是汽抚,注意這里有一個(gè)細(xì)節(jié)差別抓狭,那就是這種處理不是filter chain模式的逐步修改action得到的,而是將action帶有的副作用在中間件內(nèi)部實(shí)現(xiàn)消化造烁。換言之否过,每一個(gè)middleware都不是后一個(gè)或者前一個(gè)的return值提供者午笛。
僅僅是每個(gè)人都處理自己感興趣的部分,但是都不能修改action苗桂,同時(shí)根據(jù)需要調(diào)用next(action)將整個(gè)chain繼續(xù)下去药磺,當(dāng)然,如果你覺得在某一步出錯不用后續(xù)處理了煤伟,就可以不掉用癌佩。
最后,說一點(diǎn)便锨,所有的dispatch的掉用發(fā)端一定是store.dispatch(也就是已經(jīng)compose(f1,f2,f3..store.orgin_dispatch)),這一步不應(yīng)該產(chǎn)生返回值围辙,否則我要reducer干嘛。
Q2 為何最后還是得有原生的store.dispatch,這其實(shí)是廢話沒有這個(gè)就沒法進(jìn)行reducer通知合并action更新state了放案。
基于上面的論述姚建,我們可以確定兩件事情。 第一,middleware中承載的邏輯應(yīng)該定位為副作用操作卿叽,并且不得修改action(但是可以skip)桥胞。請記住恳守,不要亂套fp的概念考婴,這個(gè)不是monad,本身也不是f1(f2..(..)). 下面的是標(biāo)準(zhǔn)的f1(f2..(..)) ``let f1 = (x) => { x = x + 1 ;return x + 1}
undefined
let f2 = (y) => (2 * y)
undefined
let f3 = (x) => f1(f2(x))
undefined
f3(2)
6 ``
然而催烘,在真實(shí)的redux里面卻是
| f1 ? ? ? ? ? ? ? ? ? ? ? ? ?|?
| ?step process ?1 ? ? |?
-------------------- => compose(f1,f2,f3..)?
| ? step process 2 ? ? ?|
| ? ? ? ? ? ? ? ? ?|f2 | | ... |?
|------------------|
?f1內(nèi)部先做預(yù)處理沥阱,然后決定是否call f2,f2內(nèi)部重復(fù)這個(gè)過程伊群。
## 2016-5-12更新
第二考杉,middleware中的return next(action) 更準(zhǔn)確的應(yīng)該是 let ret = next(action) -- 官網(wǎng)demo里面 return ret -- 完全不必要 因?yàn)?你可以推理 也可以 寫console.log加斷點(diǎn)跟蹤,這些return 最后給誰了舰始? 答案是 store.dispatch(這里的dispatch就是你上面 applyMiddleware增強(qiáng)以后的)崇棠。所以,明白了吧丸卷,這就是一個(gè)常規(guī)的嵌套調(diào)用枕稀,mid1里面 next-》mid2 next->mid3-> ... 這樣來的最后的 next(中間件最后)其實(shí) 就是 store原生的 dispatch 到這里 我們的action終于給了 reducer。
第三谜嫉,通過上面的闡述萎坷,我可以給一個(gè)自己的結(jié)論,那就是middleware的設(shè)計(jì)定位應(yīng)該是處理和業(yè)務(wù)無關(guān)的副作用操作(比如 websocket沐兰,http,db,log等等)哆档,且它也同樣適合做 filter(不合法的action我不傳遞了) 和? interceptor(action 增強(qiáng)比如,你穿入user住闯,我附加上user的擴(kuò)展信息瓜浸,然后next給后面)
## 2016-5-12 追加更新
剛剛同事問我上面說的對應(yīng)源代碼中的執(zhí)行細(xì)節(jié)澳淑,包括但不限于何時(shí)是參數(shù)綁定,何時(shí)是函數(shù)執(zhí)行插佛。我從他的問題里面漸漸意識到偶惠,原來