在redux的流程中,會存在一些特殊的需求,好比打印action的信息來進行調試,或者要處理一個異步請求。中間件就是爲了處理這些特殊的需求而存在的。在redux的流程中,action creator 和 reducer 都是純函數,action creator返回一個包含type和payload的對象,reducer函數用來處理和返回state,應該保證他們的單一性和純度。因此可以處理這些特殊需求的只剩下dispatch了,redux的中間件就是用來對dispatch進行加強的。redux
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import logger from 'redux-logger';
import rootReducer from '../reducers';
export default function configureStore() {
return createStore(
rootReducer,
applyMiddleware(thunk, promise, logger),
);
}複製代碼
在使用createStore進行初始化的時候,若是第三個參數不爲空,或者第二參數爲一個函數,那麼就用這個enhancer函數進行初始化。createStore會被當成enhancer函數的參數傳入,並在enhancer這個函數再次使用createStore對redux進行初始化。數組
import compose from './compose';
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)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}複製代碼
createStore是初始化傳進來的,…args是初始化用的reducers和preloadedState,最終生成store對象。promise
在函數內部定義一個dispatch函數,定義一個middlewareAPI對象,將原生的getState和自定義的dispatch賦值給它。bash
middlewares.map將middlewareAPI這個對象送到每一箇中間件中,使得每一箇中間件都能經過getState訪問到整個store上的數據,而且經過閉包將自定義的dispatch函數傳到每一箇中間件中,使得最終增強過的dispatch函數能被每一箇中間件調用。閉包
compose函數用來對中間件數組chain進行處理,生成最終的dispatch函數。app
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)))
}複製代碼
上面就是compose函數,經過數組的reduce函數,將中間件進行合併。異步
compose([f, g, h]) ===> (...args) => f(g(h(...args)))函數
compose函數能夠簡化爲上面的函數,在本文中,引入了三個中間件,下面將用這三個中間件進行簡單的說明。ui
const thunk = thunk(middlewareAPI);
const promise = promise(middlewareAPI);
const logger = logger(middlewareAPI);
const chain = [thunk, promise, logger];
compose(...chain)
↓ ↓ ↓ ↓ ↓ ↓
(...args) => thunk(promise(logger(...args)))
中間件中的next參數,用來將action傳遞到下一個中間件中,經過上面的轉換能夠看出來:
thunk函數中的next等於promise(logger(..args))
promise函數中的next等於logger(...args)
最終生成的dispatch函數能夠表示爲:
dispatch = compose(...chain)(store.dispatch)
↓ ↓ ↓ ↓ ↓ ↓
dispatch = thunk(promise(logger(store.dispatch)))
由於閉包的緣由,最終生成的dispatch函數將會傳入到每一箇中間件中。複製代碼
經過上面的分析,能夠看出每個middleware中store的dispatch函數經過閉包的做用,都和compose函數最終生成的dispatch保持一致,而且原生的dispatch函數被包裹在最內層,middleware經過調用next()函數進入下一個中間件,並最終調用原生的dispatch函數實現action的分發,具體的過程以下圖this
若是在某一箇中間件中調用了store.dispatch,那麼就至關於從當前的中間件中跳出,從新執行一遍上圖中的過程。若是中間件一直粗暴的執行store.dispatch而沒有一個合適的狀態去調用next函數,那麼就會進入一個死循環中。具體的過程以下圖
以reudx-promise爲例
import isPromise from 'is-promise';
import { isFSA } from 'flux-standard-action';
export default function promiseMiddleware({ dispatch }) {
return next => action => {
if (!isFSA(action)) {
return isPromise(action) ? action.then(dispatch) : next(action);
}
return isPromise(action.payload)
? action.payload
.then(result => dispatch({ ...action, payload: result }))
.catch(error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
})
: next(action);
};
}複製代碼
在redux-promise中,若是action的payload是一個promise對象,那麼會在then中調用dispatch方法,此時就會跳出promise這個中間件,從最外層從新進入這個中間件鏈;若是action的payload是一個正常的對象,那麼會執行next函數,進入下一個中間件。