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); }; }