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
}
}
}
複製代碼
爲了便於理解,我將源碼中的箭頭函數全都改成具名函數(以fn
加上數字標記),以便於對照分析:javascript
function applyMiddleware(...middlewares) {
return function fn1(createStore) {
return function fn2(...args) {
const store = createStore(...args);
let dispatch = function fun3() {
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: function fn4(...args) {
dispatch(...args);
},
};
const chain = middlewares.map(function fn5(middleware) {
return middleware(middlewareAPI);
});
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch,
};
};
};
}
複製代碼
在詳細分析applyMiddleware
源碼以前,咱們須要知道的是,中間件middleware
的功能是對redux
提供的dispatch
進行一些擴展,或者說是加強,好比說最多見的logger
中間件,它的任務就是在dispatch
的過程當中順帶着實現一下打印日誌的任務,那麼這樣看來,其實applyMiddleware
就像名字同樣,其做用就是告訴redux
怎麼處理這些中間件,或者更具體的說,怎麼把中間件運用在redux
提供的dispatch
上。java
好了,下面開始逐條分析源碼(這裏便於定位,實際上是對照改寫後的函數)redux
applyMiddleware
函數接受一箇中間件的散列,並用rest參數收集到middlewares
這個數組中,最終會返回一個函數fn1。api
函數fn1只有兩個做用,第一是接受惟一的參數createStore
,沒錯就是redux提供的createStore
方法;第二返回一個函數fn2。若是看過前一篇文章compose的應該能夠看出,這裏運用了柯里化的思想。數組
函數fn2接受一個rest參數(結合createStore
方法能夠知道其實就是reducer
和preloadState
),同時在內部詳細處理了如何將多箇中間件和redux
的dispatch
有機結合,如下幾點逐行描述。app
利用最基本的createStore
建立一個store備用;函數
const store = createStore(...args);
複製代碼
本地建立一個dispatch
函數fn3,目前這個dispatch很是簡單,就是拋出一個錯誤。post
let dispatch = function fun3() {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
);
};
複製代碼
建立middlewareAPI
,能夠看到這個對象中只包含了兩個屬性:getState
和dispatch
。getState返回的是上面建立的store的getState方法,以便在中間件中可以獲取到state;而dispatch則是一個新的函數fn4,它接受rest參數args,並在內部調用5中提到的本地建立的dispatch(即fn3),注意這裏須要傳入參數,哪怕定義dispatch的時候並無定義。ui
const middlewareAPI = {
getState: store.getState,
dispatch: function fn4(...args) {
dispatch(...args);
},
};
複製代碼
因爲這部分嵌套比較多,因此決定引用一個logger中間件來參照:this
// logger中間件
// 第一層
const logger = store => {
// 第二層
return next => {
// 第三層
return action => {
console.group(action.type);
console.info('dispatching', action);
let result = next(action);
console.log('next state', store.getState());
console.groupEnd(action.type);
return result;
};
};
}
// source code
const chain = middlewares.map(function fn5(middleware) {
return middleware(middlewareAPI);
});
dispatch = compose(...chain)(store.dispatch);
複製代碼
對applyMiddlewares中接受到的rest參數middlewares使用一個map遍歷,處理函數fn5的工做是將middlewareAPI注入到中間件中,以logger爲例,這裏的middlewareAPI就是logger的入參store,同時返回第二層的函數(以next爲入參),因此咱們知道,chain
其實就是把全部的中間件都先行注入middlewareAPI後的以next爲入參的函數組成的數組,或者能夠說,chain中包含了一系列函數,這些函數都被統一注入了middlewareAPI,而且接受next做爲入參。
順帶提一下,由middlewareAPI也能夠看出,在編寫中間件的時候,能拿到的store的api其實就只有兩個——getState和dispatch,而且這裏的dispatch仍是看上去沒什麼用的本地建立的西貝貨dispatch,只能拋出錯誤。
接着就用到了上一篇提到的compose函數了,這裏是接受chain數組展開的散列做爲參數,返回一個由多箇中間件嵌套的函數,這個函數接受store.dispatch
做爲入參,參考logger就是第二層中的next
參數,這樣咱們就獲得了一個處理過的、包含了中間件加強功能的dispatch函數(此時已經將那個只會throw Error的dispatch替換掉了)。
若是看過compose就能夠知道,入參的順序決定執行的順序,因此咱們通常將logger中間件儘量放在applyMiddleware的後面就能夠理解了,爲了第一步就執行logger。
最終返回一個對象做爲用戶建立的store,和普通的由createStore建立的store不一樣的地方在於,這裏提供的dispatch是通過了處理的,從這一點上也能很明確看到以前提過的,中間件的做用就是對普通的dispatch進行了加強。
return {
...store, // 這個store由createStore在本函數中建立
dispatch,
};
複製代碼