Middleware 只是包裝了 store 的 dispatch 方法。技術上講,任何 middleware 能作的事情,均可能經過手動包裝 dispatch 調用來實現,可是放在同一個地方統一管理會使整個項目的擴展變的容易得多。
中間件的做用可讓咱們決定何時調用dispatch
,可能在promise
函數執行完或在action
裏面執行回調函數後,這就須要對舊的dispatch
函數進行從新包裝,讓它可以先執行中間件函數裏面的方法,並把真正的dispatch
傳遞給中間件函數:redux
let storeDispatch = stroe.dispatch;// 取出store的dispatch方法保存 // 重寫dispatch方法 stroe.dispatch = function (action) { console.log('before dispatch') stroe.dispatch(action) // 在適當的時候調用真實的dispatch方法 console.log('after dispatch') }
咱們能夠寫一個logger
中間件來進一步瞭解:數組
function logger(store){ return (dispatch)=>{ return (action)=>{ console.log("before logger"); store.dispatch(action); console.log('after logger') } } }
中間件是一個柯里化組合的函數,每一個層級都包裝有對應的函數參數供咱們調用,真正執行的dispatch
方法其實在最後一個返回的函數裏面。promise
扯了這麼多還沒看看真正的applyMiddleware
函數長什麼樣子:app
function applyMiddleware(middlewares) { // @params 中間件數組 return function (createStore) { // @params 建立store函數 return function (reudcer) { // @params reducer return store; // @return 返回 createStore(reducer) } } }
applyMiddleware
也是一個柯里化組合的函數,不過最終返回的是一個store
,照上面說的,redux
中間件處理的是dispatch
方法,這裏也把store
的dispatch
方法從新包裝一下:函數
function applyMiddleware(middleware) { return function (createStore) { return function (reudcer) { let store = createStore(reudcer); let dispatch = ()=>{throw new Error("dispatch 還不能用,還沒改形成next方法")}; dispatch = middleware(store)(store.dispatch); // 改造包裝後的dispatch; 對應logger(store)(dispatch) return { ...store, dispatch } } } } // 根據applyMiddleware須要返回的函數傳入對應的值 applyMiddleware(logger)(createStore)(reducer);
這樣每次在組件中調用store
的dispatch
方法時,其實調用的是通過中間件logger
包裝後的dispatch
:spa
function dispatch(action){ console.log('before logger') store.dispatch(action) // 這裏纔是真實調用store.dispatch; console.log('after logger') }
多箇中間件的狀況比較複雜,須要保證每一箇中間件方法都能執行,而且可以像洋蔥模型同樣,dispatch
方法可以在最裏面執行:code
function applyMiddleware(...middlewares) { return function (createStore) { return function (reudcer) { const store = createStore(reudcer) const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args), } // 將中間件函數先遍歷,執行裏面的方法並返回,獲得一個dispatch方法的中間件數組 const chain = middlewares.map((middleware) => middleware(middlewareAPI)) // compose函數會將包含了dispatch方法的中間件數組組合成一個嵌套的包裝函數返回 const dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch, } } } }
這裏compose
方法把中間件裏面的dispatch
方法給包裝到了一塊兒,讓中間件可以一層層往裏執行:中間件
//compose function compose(...funcs) { if (funcs.length === 0) { return (args) => args } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, next) => (...args) => a(next(...args))) }
這裏的reduce
很差理解,把它拆成函數,可能會好理解一點:get
function compose(...middlewarw) { return function(storeDispatch) { function dispatch(index, action) { let fn = middlewarw[index] // 回調裏面的 next 方法,執行的是下一個中間件函數 let next = () => dispatch(index + 1, action) // 若是還有下一個中間件就繼續執行,並把action傳進去,不然執行store.dispatch方法 fn ? fn(next)(action) : storeDispatch(action) } return (action) => dispatch(0, action) } };
另外經常使用到的中間件,這裏也寫下它的源碼實現:回調函數
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;
function isPromise(obj) { return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; } export default function promiseMiddleware({ dispatch }) { return 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); }; }