摘要:最后看一下這時候執行返回,如下調用執行循序調用第層中間件返回即調用第層中間件返回即調用根返回即調用一個例子讀懂上文提到是個柯里化函數,可以看成是將所有函數合并成一個函數并返回的函數。
由于一直用業界封裝好的如redux-logger、redux-thunk此類的中間件,并沒有深入去了解過redux中間件的實現方式。正好前些時間有個需求需要對action執行時做一些封裝,于是借此了解了下Redux Middleware的原理。* 中間件概念
首先簡單提下什么是中間件,該部分與下文關系不大,可以跳過。來看眼這個經典的圖。
不難發現:
不使用middleware時,在dispatch(action)時會執行rootReducer,并根據action的type更新返回相應的state。
而在使用middleware時,簡言之,middleware會將我們當前的action做相應的處理,隨后再交付rootReducer執行。
簡單實現原理比如現有一個action如下:
function getData() { return { api: "/cgi/getData", type: [GET_DATA, GET_DATA_SUCCESS, GET_DATA_FAIL] } }
我們希望執行該action時可以發起相應請求,并且根據請求結果由定義的type匹配到相應的reducer,那么可以自定義方法處理該action,因此該方法封裝成中間件之前可能是這樣的:
async function dispatchPre(action, dispatch) { const api = action.api; const [ fetching_type, success_type, fail_type] = action.type; // 拉取數據 const res = await request(api); // 拉取時狀態 dispatch({type: fetching_type}); // 成功時狀態 if (res.success) { dispatch({type: success_type, data: res.data}); console.log("GET_SUCCESS"); } // 失敗時狀態 if (res.fail) { dispatch({type: fail_type}); console.log("GET_FAIL"); }; } // 調用: dispatchPre(action(), dispatch)
那如何封裝成中間件,讓我們在可以直接在dispatch(action)時就做到這樣呢?可能會首先想到改變dispatch指向
// 儲存原來的dispatch const dispatch = store.dispatch; // 改變dispatch指向 store.dispatch = dispatchPre; // 重命名 const next = dispatch;
截止到這我們已經了解了中間件的基本原理了~
源碼分析了解了基本原理能有助于我們更快地讀懂middleware的源碼。
業務中,一般我們會這樣添加中間件并使用。
createStore(rootReducer, applyMiddleware.apply(null, [...middlewares]))
接下來我們可以重點關注這兩個函數createStore、applyMiddleware
CreateStore// 摘至createStore export function createStore(reducer, rootState, enhance) { ... if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } /* 若使用中間件,這里 enhancer 即為 applyMiddleware() 若有enhance,直接返回一個增強的createStore方法,可以類比成react的高階函數 */ return enhancer(createStore)(reducer, preloadedState) } ... }ApplyMiddleware
再看看applyMiddleware做了什么,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) } // 1、將store對象的基本方法傳遞給中間件并依次調用中間件 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 2、改變dispatch指向,并將最初的dispatch傳遞給compose dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }執行步驟
根據源碼,我們可以將其主要功能按步驟劃分如下:
1、依次執行middleware。
將middleware執行后返回的函數合并到一個chain數組,這里我們有必要看看標準middleware的定義格式,如下
export default store => next => action => {} // 即 function (store) { return function(next) { return function (action) { return {} } } }
那么此時合并的chain結構如下
[ ..., function(next) { return function (action) { return {} } } ]
2、改變dispatch指向。
想必你也注意到了compose函數,compose函數如下:
[...chain].reduce((a, b) => (...args) => a(b(...args)))
實際就是一個柯里化函數,即將所有的middleware合并成一個middleware,并在最后一個middleware中傳入當前的dispatch。
*compose可能會看得有點蒙,不理解柯里化函數的同學可以跳到一個例子讀懂compose先了解下。
// 假設chain如下: chain = [ a: next => action => { console.log("第1層中間件") return next(action) } b: next => action => { console.log("第2層中間件") return next(action) } c: next => action => { console.log("根dispatch") return next(action) } ]
調用compose(...chain)(store.dispatch)后返回a(b(c(dispatch)))。
可以發現已經將所有middleware串聯起來了,并同時修改了dispatch的指向。
最后看一下這時候compose執行返回,如下
dispatch = a(b(c(dispatch))) // 調用dispatch(action) // 執行循序 /* 1. 調用 a(b(c(dispatch)))(action) __print__: 第1層中間件 2. 返回 a: next(action) 即b(c(dispatch))(action) 3. 調用 b(c(dispatch))(action) __print__: 第2層中間件 4. 返回 b: next(action) 即c(dispatch)(action) 5. 調用 c(dispatch)(action) __print__: 根dispatch 6. 返回 c: next(action) 即dispatch(action) 7. 調用 dispatch(action) */*一個例子讀懂compose
上文提到compose是個柯里化函數,可以看成是將所有函數合并成一個函數并返回的函數。
例如先定義3個方法
function A(x){ return x + "a" } function B(y){ return y + "b" } function C(){ return "c" } var d = [...A, b, C].reduce((a, b) => (d) => {console.log(d, a, b); a(b(d))}) d // 打印d // f (d) { console.log(d, a, b); return a(b(d)) } d("d") // 調用d /* * d * f(d) { console.log(d, a, b); return a(b(d)) } * f C() { return "c" } */ /* * c * f A(x) { return x + "a" } * f B(y) { return y + "b" } */
不難發現,使用閉包,在調用d的時候,將a、b函數儲存在了內存中,調用時會依次將數組從右至左的函數返回做為參數傳遞給下一個函數使用
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/102239.html
摘要:另外,內置的函數在經過一系列校驗后,觸發,之后被更改,之后依次調用監聽,完成整個狀態樹的更新。總而言之,遵守這套規范并不是強制性的,但是項目一旦稍微復雜一些,這樣做的好處就可以充分彰顯出來。 這一篇是接上一篇react進階漫談的第二篇,這一篇主要分析redux的思想和應用,同樣參考了網絡上的大量資料,但代碼同樣都是自己嘗試實踐所得,在這里分享出來,僅供一起學習(上一篇地址:個人博客/s...
摘要:另外,內置的函數在經過一系列校驗后,觸發,之后被更改,之后依次調用監聽,完成整個狀態樹的更新。總而言之,遵守這套規范并不是強制性的,但是項目一旦稍微復雜一些,這樣做的好處就可以充分彰顯出來。 這一篇是接上一篇react進階漫談的第二篇,這一篇主要分析redux的思想和應用,同樣參考了網絡上的大量資料,但代碼同樣都是自己嘗試實踐所得,在這里分享出來,僅供一起學習(上一篇地址:個人博客/s...
摘要:在函數式編程中,異步操作修改全局變量等與函數外部環境發生的交互叫做副作用通常認為這些操作是邪惡骯臟的,并且也是導致的源頭。 注:這篇是17年1月的文章,搬運自本人 blog... https://github.com/BuptStEve/... 零、前言 在上一篇中介紹了 Redux 的各項基礎 api。接著一步一步地介紹如何與 React 進行結合,并從引入過程中遇到的各個痛點引出 ...
摘要:的中間件主要是通過模塊實現的。返回的也是一個對象這個其實就是,各個中間件的最底層第三層的哪個函數組成的圓環函數構成的這就是對源碼的一個整體解讀,水平有限,歡迎拍磚。后續的源碼解讀和測試例子可以關注源碼解讀倉庫 applyMiddleware源碼解析 中間件機制在redux中是強大且便捷的,利用redux的中間件我們能夠實現日志記錄,異步調用等多種十分實用的功能。redux的中間件主要是...
閱讀 4581·2021-09-10 11:22
閱讀 538·2019-08-30 11:17
閱讀 2573·2019-08-30 11:03
閱讀 437·2019-08-29 11:18
閱讀 3462·2019-08-28 17:59
閱讀 3225·2019-08-26 13:40
閱讀 3169·2019-08-26 10:29
閱讀 1142·2019-08-26 10:14