若是须要在dispatch先后分别打印出action,和action后的state,咱们须要怎么作
实现方案以下:
首先咱们想到在先后分别添加console,每次调用都在调用先后加consolees6
在每次dispatch先后加consoleredux
console.log("start ",action) store.dispatch(action) console.log("end ",store.getState())
可是这样每一次调用都要多写两行代码,总的来讲会出现不少console,最容易想到的就是把这段代码封装到一个函数里,每次须要的时候调用函数便可api
定义成方法,在须要dispatch时调用方法app
function dispatchAndLog(store,action){ console.log("start ",action) store.dispatch(action) console.log("end ",store.getState()) }
调用dispatch的地方改成调用以下代码函数
dispatchAndLog(store,action)
这样来讲貌似能够了,可是每次都调用一个非api方法,新人不易理解,仍是应该调用原有api,而后又有了修改默认dispatch的方案spa
修改默认的dispatch方法
在之后的调用中直接调用store.dispatch
便可插件
const next = store.dispatch store.dispatch = fuction dispatchAndLog(store,action){ console.log("start ",action) let result = next(action) console.log("end ",store.getState()) return result }
这样看起来蛮好的,可是若是我如今不只须要这一个打印日志的功能还须要一个能提供报错信息的功能
咱们可能想到以下方案日志
添加出错报告功能code
function patchStoreToAddLog(store,action){ const next = store.dispatch store.dispatch = function dispatchAndLog(store,action){ console.log("start ",action) let result = next(action) console.log("end ",store.getState()) return result } } function patchStoreToAddCrashReporting(store,action){ const next = store.dispatch store.dispatch = function dispatchAndCrashReporting(store,action){ try { return next(action) } catch (err) { console.error('Caught an exception!', err) // 把错误信息发送到其余监控系统 throw err } } }
这样即要日志,又要报错的时候依次调用两个函数便可中间件
patchStoreToAddLog(store,action) patchStoreToAddCrashReporting(store,action)
这样仍是原来问题每次调用都要写两个,麻烦
更好方案以下:
使用一个功能返回一个函数,返回的函数用于处理插件的逻辑
function logger(store) { const next = store.dispatch // Previously: // store.dispatch = function dispatchAndLog(action) { return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result; } } function crashReporting(store){ const next = store.dispatch return function dispatchAndCrashReporting(action){ try { return next(action) } catch (err) { console.error('Caught an exception!', err) // 把错误信息发送到其余监控系统 throw err } } }
调用方法以下
logger(store)(action) crashReporting(store)(action)
仔细观察实际上是有规律的,能够写一个函数来遍历执行全部的插件
function applyMiddlewareByMonkeypatching(store, middlewares){ middlewares = middlewares.slice() middlewares.reverse() //确保卸载后面的插件包在里面,后面执行 middlewares.forEach(middleware=> //每个middleware均可以直接调用前一个 middleware 包装过的 store.dispatch store.dispatch = middleware(store) ) }
调用以下
applyMiddlewareByMonkeypatching(store,[logger,crashReporting])
等于以下代码
// according to https://redux.js.org/api-reference/store#dispatch // store.dispatch(action) 返回新的state 即新的store store.dispatch = logger(crashReporting(store)) //也就等同于以下代码 store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action) let result = function dispatchAndCrashReporting(action){ try { return next(action) } catch (err) { console.error('Caught an exception!', err) // 把错误信息发送到其余监控系统 throw err } } console.log('next state', store.getState()) return result; }
这种方法已经很好了,可是它依旧是修改原来的store.dispatch方法,咱们能够想办法不修改默认的store.dispatch
方法
更进一步
function logger(store) { return function wrapperDispatchAndLog(next){ return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result; } } } // es6写法 const logger = store => next => action => { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result; } } } // 修改遍历函数 function applyMiddleware (store, middlewares){ middlewares = middlewares.splice() middlewares.reverse() let dispatch = store.dispatch middlewares.forEach(middleware=>{ dispatch = middleware(store)(dispatch) }) return Object.assign({},store,dispatch) } // 调用方法 // 返回通过middleware包装过的含有新dispatch的store applyMiddleware(store,[logger,crashReporting])