本文地址git
通常使用了中間件的 redux 初始化是下面這樣的github
function configureStore(initialState) {
return {
...createStore(
reducer,
initialState,
applyMiddleware(middleware1, middleware2, middleware3)
)
};
}
const store = configureStore({});
複製代碼
redux 中間件是一個函數,形式redux
const middleware1 = store => next => action => {
console.log("before", 1);
next(action);
console.log("after", 1);
};
const middleware2 = store => next => action => {
console.log("before", 2);
next(action);
console.log("after", 2);
};
const middleware3 = store => next => action => {
console.log("before", 3);
next(action);
console.log("after", 3);
};
複製代碼
須要作到bash
保證每個中間件內的 store 引用都是最新的,而且是同一個app
鏈接起來。執行一次 dispatch,會依次執行每個中間件異步
代碼解釋函數
function applyMiddleWare(...middlerWares) {
return createStore => (...args) => {
const store = createStore(...args);
let dispatch = () => {};
const middlerWareAPI = {
dispatch: action => dispatch(action),
getState: () => store.getState
};
const chain = middlerWares.map(middleWare => chain(middlerWareAPI));
dispatch = chain.reduce((f, g) => (...args) => f(g(...args)))(
store.dispatch
);
return {
...store,
dispatch
};
};
}
複製代碼
從上面能夠看到,一箇中間件函數要最後完成,須要再執行前通過兩次的初始化(分別傳入 store 和 next 方法),而後到最後的調用測試
const chain = middlerWares.map(middleWare => chain(middlerWareAPI));
複製代碼
將store
兩個方法傳遞給中間件,全部中間件內都是同一份store
fetch
dispatch
一次就執行全部的中間件// 這裏其實就是compoose的實現
dispatch = chain.reduce((f, g) => (...args) => f(g(...args)))(store.dispatch);
// 等價於相似 下面的形式
dispatch = middleWare1(middleWare2(middleWare3(dispatch)));
複製代碼
咱們知道,通過第一步的初始化,對於middleWare1
函數,可見next
參數就是指向了middleWare2(dispatch)
。只有咱們的next
調用了纔會執行後面的中間件。而到了最後一箇中間件middleWare3
,它的next
參數就是 redux 對應的dispatch
函數了。從而最終把咱們的action
派發到store
中去。而後調用棧依次返回。就像一個剝洋蔥同樣的東西。ui
因此若是咱們把開頭的 3 箇中間件組合起來運行的話,
輸出是
before 1
before 2
before 3
通過reducer // reducer中的log測試
after 3
after 2
after 1
複製代碼
通過上面的代碼,咱們還能夠發現applyMiddleWare
其實也是一個高階函數。applyMiddleware(middleware1, middleware2, middleware3)
執行後,是一個接收createStore
參數的函數。在createStore
裏面又是如何操做的。源碼:
function createStore(reducer, preloadedState, enhancer) {
// .....enhancer就是上面提到的 applyMiddleware(middleware1, middleware2, middleware3)返回結果
if (typeof enhancer !== "undefined") {
return enhancer(createStore)(reducer, preloadedState);
}
// ....
複製代碼
其實就是把原始的createStore
再傳進入,進行建立store
。返回一個更牛逼的store
,這個store
其實只是重寫了dispatch
方法。
一、 中間件內部必須調用next
方法。才能調用下一個中間件而且到達 action
二、中間件內部若是調用了dispatch
(重寫後的)。不加處理就會死循環,至關於這個洋蔥剝到中間又開始剝了。
dispatch
何時用呢?這就提到一個大名鼎鼎的庫redux-thunk
。它的源碼裏面就是使用了這個dispatch
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;
複製代碼
redux-thunk
相信都很熟悉,通常用於處理編寫異步邏輯下。它的源碼更是牛逼。只有十幾行。從上面能夠看出它也是一箇中間件,它的邏輯就是容許你dispatch
一個函數,當你 dispatch 一個函數的時候,就直接執行它,並傳入了dispatch
(注意這個 是 dispatch 不是 前面中間件提到的 next)和getState
方法
const fetchApi = (...args) => (dispatch, getState) => {
setTimeout(() => {
dispatch({ type: "xx", payload: "good" });
}, 7000);
};
dispatch(fetchApi("xxx"));
複製代碼
經過前面的分析,傳進thunk
內的dispatch
參數就是通過包裝的dispatch
,因此當咱們去分發一個同步的普通action
的時候,它又能通過咱們的其他普通中間件邏輯的處理了
redux
的applyMiddleWare
函數和redux-thunk
。代碼很簡短,卻隱藏着大智慧