原文連接前端
在業務中須要打印每個 action 信息來調試,又或者但願 dispatch 或 reducer 擁有異步請求的功能。面對這些場景時,一個個修改 dispatch 或 reducer 代碼有些乏力,咱們須要一個可組合的、自由增減的插件機制,Redux 借鑑了 Koa 中 middleware 的思想,利用它咱們能夠在前端應用中便捷地實現如日誌打印、異步請求等功能。react
好比在項目中,進行了以下調用後,redux 就集成了 thunk 函數調用以及打印日誌的功能。git
import thunk from 'redux-thunk'
import logger from '../middleware/logger'
const enhancer = applyMiddleware(thunk, logger), // 以 redux-thunk、logger 中間件爲例介紹中間件的使用
const store = createStore(rootReducer, enhancer)
複製代碼
下面追本溯源,來分析下源碼。github
export default function createStore(reducer, preloadedState, enhancer) {
// 經過下面代碼能夠發現,若是 createStore 傳入 2 個參數,第二個參數至關於就是 enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
return enhancer(createStore)(reducer, preloadedState)
}
...
}
複製代碼
由上述 createStore 源碼發現,applyMiddleware 會進行 applyMiddleware(thunk, logger)(createStore)(reducer, preloadedState)
的調用。redux
applyMiddleware 源碼以下數據結構
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = store.dispatch
let chain = []
const middlewareAPI = {
getState: store.getState, // 調用 redux 原生方法,獲取狀態
dispatch: (...args) => dispatch(...args) // 調用 redux 原生 dispatch 方法
}
// 串行 middleware
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch // 返回加工過的 dispatch
}
}
}
複製代碼
能夠發現 applyMiddleware 的做用其實就是返回加工過的 dispatch,下面會着重分析 middlewares 是如何串行起來的以及 dispatch 是如何被加工的。app
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
複製代碼
觀察上述代碼後發現每一個 middleware 都會傳入參數 middlewareAPI,來看下中間件 logger 的源碼 以及 redux-thunk 的源碼, 發現中間件接受的第一個參數正是 ({ dispatch, getState })異步
// logger 源碼
export default ({ dispatch, getState }) => next => action => {
console.log(action)
return next(action) // 經 compose 源碼分析,此處 next 爲 Store.dispatch
}
複製代碼
// redux-thunk 源碼
export default ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch)
}
return next(action) // 此處 next 爲 logger 中間件返回的 (action) => {} 函數
}
複製代碼
接着上個小節,在 dispatch = compose(...chain)(store.dispatch)
中發現了 compose 函數,來看下 compose 的源碼函數
export default function compose(...funcs) {
// ...
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製代碼
compose 源碼中的 funcs.reduce((a, b) => (...args) => a(b(...args)))
算是比較重要的一句,它的做用是返回組合參數後的函數,好比 compose(f, g, h) 等價於 (...args) => f(g(h(...args))),效果圖以下所示,調用 this.props.dispatch() 後,會調用相應的中間件,最終會調用 redux 原生的 store.dispatch(),而且能夠看到中間件調用的形式相似數據結構中的棧(先進後出)。源碼分析
拿上個小節提到的 logger、redux-thunk 中間件爲例,其 middleware 的內部串行調用方式以下,從而完成了 dispatch 功能的加強(支持如 this.props.dispatch(func)
的調用以及日誌功能)。具體能夠看 項目中的運用
action => {
if (typeof action === 'function') {
return action(dispatch)
}
return (action => {
console.log(action)
return store.dispatch(action)
})(action)
}
複製代碼
深刻React技術棧