摘要:假設等于,其中,,是三個中間件,等于,那么可以簡化為。最終返回中的方法以及經過中間件包裝處理過的方法。以此類推,第二個返回的就是第一個中間件的形參。根據(jù)這個的討論,在中間件頂層調用了,結果導致無法執(zhí)行后面的中間件。
redux 主要包含 5 個方法,分別是:
createStore
combineReducers
bindActionCreators
applyMiddleware
compose
今天主要講解下 applyMiddleware 和 compose 這兩個方法。在 redux 中引入了中間件的概念,沒錯如果你使用過 Express 或者 Koa 的話,一定不會對中間件陌生。我們知道,在 Koa 中,串聯(lián)各個中間件的正是 compose 方法,所以在 redux 中也同樣使用了這個命名,作用也是串聯(lián)所有中間件。
reduce 用法在正式講解前,我們先來看下 reduce 的用法。根據(jù) MDN 上的解釋,
reduce() 方法是對累加器和數(shù)組中的每個元素(從左到右)應用一個函數(shù),將其減少為單個值。
arr.reduce(callback[, initialValue])
callback
執(zhí)行數(shù)組中每個值的函數(shù),包含四個參數(shù):
accumulator:累加器累加回調的返回值; 它是上一次調用回調時返回的累積值,或 initialValue
currentValue:數(shù)組中正在處理的元素。
currentIndex:數(shù)組中正在處理的當前元素的索引。 如果提供了initialValue,則索引號為0,否則為索引為1。
array:調用 reduce 的數(shù)組
initialValue
用作第一個調用 callback的第一個參數(shù)的值。 如果沒有提供初始值,則將使用數(shù)組中的第一個元素。 在沒有初始值的空數(shù)組上調用 reduce 將報錯。
函數(shù)累計處理的結果
compose 分析有了上面 reduce 的基礎,我們再來看下 compose 的代碼。compose 的代碼很簡單,10行代碼左右,但你看到 reduce 部分的時候,估計會一臉懵逼,短短的一行代碼看上去卻很繞。
/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @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))). */ 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))) }
看注釋,它的作用應該是
執(zhí)行 compose(f, g, h) 得到 (...args) => f(g(h(...args)))
我們來推導下,它是怎么得出這個結果的。假設 funcs 等于 [f1, f2, f3],其中 f1,f2,f3 是三個中間件,(a, b) => (..args) => a(b(...args)) 等于 f,那么 funcs.reduce((a, b) => (...args) => a(b(...args))) 可以簡化為 [f1, f2, f3].reduce(f)。
第 1 次執(zhí)行 f:
a = f1 b = f2 返回 (...args) => f1(f2(..args))
第 2 次執(zhí)行 f:
a = (...args) => f1(f2(...args)) b = f3 返回 (...args) => a(f3(...args)) = f1(f2(f3(...args)))
通過上面的推導,證實了先前得出的結論
compise(f, g, h) = (...args) => f(g(h(...args)))applyMiddleware 分析
通過上面的分析,我們知道 compose 是對中間件的串聯(lián),那么 applyMiddleware 就是對中間件的應用了。最終返回 createStore 中的方法以及經過中間件包裝處理過的 dispatch 方法。
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.` ) } let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
我們通過一個具體的中間件 redux-thunk,來查看它內部到底是怎么來執(zhí)行加載的中間件的。
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === "function") { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
中間件中包含了三個箭頭函數(shù),在 applyMiddleware 中的 map 操作后,返回了第二層箭頭函數(shù),所以 chain 中存儲的是各個中間件的第二層函數(shù)。
根據(jù) compose 的分析,
dispatch = compose(...chain)(store.dispatch) 等于 dispatch = f1(f2(f3(store.dispatch)))
我們先執(zhí)行第三個中間件,并把返回結果作為第二個中間件的入參繼續(xù)執(zhí)行,以此類推,下一個中間件的入參是上一個中間件的返回。如果說這里第三個中間件是上面的 redux-thunk,那么函數(shù)中的 next 就是 store.dispatch,返回第三個箭頭函數(shù) action。這里返回的第三個箭頭函數(shù),就是第二個中間件的 next 形參。以此類推,第二個返回的 action 就是第一個中間件的 next 形參。但是這里都還沒真正開始執(zhí)行中間件。
當我們外部調用 store.dispatch(action) 方法的時候,才要真正開始執(zhí)行各個中間件。首先執(zhí)行中間件 f1,當執(zhí)行到 next 的時候,開始執(zhí)行第二個中間件 f2,以此類推直到最后一個中間件,調用原生 store.dispatch 方法。
之所以要寫這么繞,也是為了符合 redux 單一數(shù)據(jù)源的原則,applyMiddleware 的寫法保證了 action 的流向,而且每一步的數(shù)據(jù)變化都是可以追蹤的。
其他對比了 4.0.0-beta.1 之前版本的 applyMiddleware 的區(qū)別,發(fā)現(xiàn)內部 dispatch 從之前的 store.dispatch 改成了現(xiàn)在的直接拋出一個錯誤。根據(jù)這個 issues 的討論,在中間件頂層調用了 store.dispatch,結果導致無法執(zhí)行后面的中間件。這個調用應該是在處理 map 操作的時候執(zhí)行的,此時的 applyMiddleware 還沒執(zhí)行完,store.dispatch 調用的還是原生 createStroe 中的方法才導致的這個問題。
另外如果在中間件中即 action 層使用 dispatch 會怎樣呢?我們知道我們可以通過 next 進入到下個中間件,那如果調用 store.dispatch 的話又會從外層重新來一遍,假如這個中間件內部只是粗暴的調用 store.dispatch(action) 的話,就會形成死循環(huán)。如下圖所示
參考redux middleware 詳解Dispatching in a middleware before applyMiddleware completes
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/93857.html
摘要:調用鏈中最后一個會接受真實的的方法作為參數(shù),并借此結束調用鏈??偨Y我們常用的一般是除了和之外的方法,那個理解明白了,對于以后出現(xiàn)的問題會有很大幫助,本文只是針對最基礎的進行解析,之后有機會繼續(xù)解析對他的封裝 前言 雖然一直使用redux+react-redux,但是并沒有真正去講redux最基礎的部分理解透徹,我覺得理解明白redux會對react-redux有一個透徹的理解。 其實,...
摘要:實現(xiàn)一個先不考慮中間件,實現(xiàn)一個簡潔的實現(xiàn)是最主要的一個了,通過可以創(chuàng)建一個用來存放應用中所有的,一個應用只能有一個。方法是用來把每一個用方法包裹一下,因為可能只是返回一個具有屬性的對象,只有用執(zhí)行才有意義。正好可以利用的特性實現(xiàn)這個效果。 實現(xiàn)一個redux 先不考慮中間件,實現(xiàn)一個簡潔的redux 實現(xiàn)createStore createStore是redux最主要的一個API了,...
摘要:如果想學習項目的底層建設,建議先去學習官網案例,之后在學習的使用中間件介紹目的是提供第三方插件的模式,改變的過程。 前言 React/Redux項目結束后,當我在研究react-router源碼的時候發(fā)現(xiàn)當中有一部分含中間件的思想,所以才想把中間件重新梳理一遍;在之前看redux了解到中間件,redux層面中間件的理解對項目前期比較有幫助,雖然項目中后期基本可以忽略這層概念;現(xiàn)在對這部...
摘要:概念是一個狀態(tài)管理容器使用可以更好的管理和監(jiān)測組件之間需要通信的數(shù)據(jù)。參考源碼參考鏈接 redux概念 redux是一個狀態(tài)管理容器,使用redux可以更好的管理和監(jiān)測組件之間需要通信的數(shù)據(jù)。 redux基本原則 單一數(shù)據(jù)源 在redux中,整個應用保持一個數(shù)據(jù)源,數(shù)據(jù)源是一個樹形的結構 狀態(tài)只讀 狀態(tài)只讀意思是不能直接修改,需要通過dispatch action方式才可以,返回的是一...
摘要:數(shù)組為新的數(shù)組,包含了方法將新的和結合起來,生成一個新的方法返回的新增了一個方法,這個新的方法是改裝過的,也就是封裝了中間件的執(zhí)行。 書籍完整目錄 3.3 理解 Redux 中間件 showImg(https://segmentfault.com/img/bVymkt); 這一小節(jié)會講解 redux 中間件的原理,為下一節(jié)講解 redux 異步 action 做鋪墊,主要內容為: ...
閱讀 1322·2023-04-25 20:56
閱讀 2470·2023-04-25 14:42
閱讀 1106·2023-04-25 14:06
閱讀 2933·2021-10-14 09:42
閱讀 2216·2021-09-22 16:03
閱讀 1063·2021-09-13 10:30
閱讀 1409·2019-08-29 15:41
閱讀 1882·2019-08-29 12:55