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