若是須要在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])