它提供了一個(gè)分類處理action的機(jī)會(huì)。在中間件中,可以檢閱每個(gè)流過(guò)的action,挑選出特定類型的action進(jìn)行相應(yīng)的操作,提供一次改變action的機(jī)會(huì)。
通過(guò)中間件,我們可以在dispatch action之后,到達(dá)reducer之前,搞點(diǎn)事情。比如:打印,報(bào)錯(cuò),跟異步API通信等等
下面,讓我們一步步來(lái)理解下中間件是如何實(shí)現(xiàn)的:
我們從createStore.js調(diào)用中間件的地方開(kāi)始理解:
export default function createStore(reducer, preloadedState, enhancer) {
// 如果第二個(gè)參數(shù)沒(méi)有傳入初始的state,而是傳入了enhancer
// applyMiddleware調(diào)用的返回值, 那就將第二個(gè)參數(shù),preloadedState賦值給enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 當(dāng)enhancer不為空且為函數(shù)時(shí),就執(zhí)行該函數(shù),并return回去,作為creatStore的返回值。
return enhancer(createStore)(reducer, preloadedState)
}
理解middleware機(jī)制
redux提供了applyMiddleware方法來(lái)加載中間件,先來(lái)看下總覽:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
....
....
....
return {
...store,
dispatch
}
}
applyMiddleware接受不定數(shù)量的中間件,然后返回一個(gè)接受一個(gè)creatStore作為參數(shù)的函數(shù)的函數(shù)?;厝ピ倏聪挛覀兊?code>creatStore:
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 當(dāng)enhancer不為空且為函數(shù)時(shí),就執(zhí)行該函數(shù),并return回去,作為creatStore的返回值。
return enhancer(createStore)(reducer, preloadedState)
}
當(dāng)enhancer不為空且為函數(shù)時(shí),就執(zhí)行該函數(shù),并return回去,作為creatStore的返回值。回憶下我們?cè)趓edux中怎樣使用中間件的
const store = createStore(reducers, applyMiddleware(a, b, c));
其實(shí)enhancer就是applyMiddleware的結(jié)果。也就是createStore => (...args) => {}
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
// 聲明一個(gè)dispatch
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)
}
// 第一步把middlewares里的函數(shù)帶著middlewareAPI這個(gè)參數(shù)都挨個(gè)執(zhí)行,
//然后保存在chain數(shù)組中。也就是middleware中的第一個(gè)參數(shù),返回 next => action => {...}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 問(wèn)題:compose是如何將中間件串聯(lián)在一起的?
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
compose.js怎么組合函數(shù)的呢?
return funcs.reduce((a, b) => (...args) => a(b(...args)))
一臉蒙蔽,這是什么?
現(xiàn)在假設(shè)我們需要組合兩個(gè)函數(shù):
var f1 = function(a) {
return a * 10;
}
var f2 = function(a) {
return a + 1;
}
function compose(f1, f2) {
return function(x) {
return f1(f2(x))
}
}
var number = compose(f1, f2)(4)
讓代碼從右向左運(yùn)行變成f1(f2(x)),4 -> f2 -> f1 -> 50
現(xiàn)在我們修改下compose函數(shù)
let compose = (...funs) => (result) => {
for (var i = funs.length - 1; i > -1; i--) {
result = funs[i].call(this, result);
}
return result;
}
var number = compose(f1, f2)(4)
我們?cè)倩氐絩edux的compose.js
return funcs.reduce((a, b) => (...args) => a(b(...args)))
拆分上面的源碼步驟如下:
- 返回
(...args) => a(b(...args)且與下一個(gè)c函數(shù)做計(jì)算 - a 等于
(...args) => a(b(...args),b 等于 c 這個(gè)function , 然后再一次做(...args) => a(b(...args))計(jì)算,就形成了(c(...args)) => a(b(...args))返回a(b(c(...args))) - 所以compose其實(shí)就是實(shí)現(xiàn)了組合運(yùn)算
(...args) => f(g(h(...args))).
再回頭看下applyMiddleware的方法:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
// 聲明一個(gè)dispatch
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)
}
// 第一步把middlewares里的函數(shù)帶著middlewareAPI這個(gè)參數(shù)都挨個(gè)執(zhí)行,
//然后保存在chain數(shù)組中。也就是middleware中的第一個(gè)參數(shù),返回 next => action => {...}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 在中間件中調(diào)用dispatch的時(shí)候, 其實(shí)就是調(diào)用了這個(gè)dispatch,而不是一開(kāi)始聲明的邏輯
// 這個(gè)dispatch是已經(jīng)經(jīng)過(guò)compose的包裝的函數(shù)
// const composeRes = compose(...chain);
// 第二行通過(guò)傳入store.dispatch,
// 這個(gè)store.dispatch就是最后一個(gè) next => action => {}的next參數(shù)
// 所以中間件其實(shí)就是改裝dispatch
// dispatch作為有中間件的store的dispatch屬性輸出
// 當(dāng)用戶調(diào)用dispatch時(shí), 中間件就會(huì)一個(gè)一個(gè)
// 執(zhí)行完邏輯后, 將執(zhí)行權(quán)給下一個(gè), 直到原始的store.dispacth, 最后計(jì)算出新的state
// 問(wèn)題:compose是如何將中間件串聯(lián)在一起的?
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
總結(jié)下:
- 第一步把
middlewares數(shù)組里的函數(shù)帶著middlewareAPI這個(gè)參數(shù)都挨個(gè)執(zhí)行個(gè)遍且,且保存在chain數(shù)組中。也就是middleware中的第一個(gè)參數(shù),返回next => action => {...} - 就是上面
composeJs中組合chain中的函數(shù)。變成f1(f2(f3(store.dispatch))),
并且最終保存在dispatch 變量中。里面的函數(shù)最終會(huì)返回action => {...}這個(gè)函數(shù),
作為上一個(gè)函數(shù)的next參數(shù)保存在上個(gè)函數(shù)的scope中,所以每個(gè)middleware 中必須執(zhí)行next(action)才能進(jìn)入下個(gè)middleware。
而最后一個(gè)middleware傳入的next正是原始的store.dispatch。