書籍完整目錄es6
這一小節會講解 redux 中間件的原理,爲下一節講解 redux 異步 action 作鋪墊,主要內容爲:express
Redux 中間件是什麼redux
使用 Redux 中間件segmentfault
logger 中間件結構分析後端
applyMiddleware數組
中間件的執行過程閉包
Redux moddleware provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.app
redux 提供了相似後端 Express 的中間件概念,本質的目的是提供第三方插件的模式,自定義攔截action -> reducer
的過程。變爲 action -> middlewares -> reducer
。這種機制能夠讓咱們改變數據流,實現如異步 action ,action 過濾,日誌輸出,異常報告等功能。異步
Redux 提供了一個叫 applyMiddleware
的方法,能夠應用多箇中間件,以日誌輸出中間件爲例async
import { createStore, applyMiddleware } from 'redux' import createLogger from 'redux-logger' import rootReducer from './reducers' const loggerMiddleware = createLogger() const initialState = {} return createStore( rootReducer, initialState, applyMiddleware( loggerMiddleware ) )
看看 redux-logger 的源碼結構
function createLogger(options = {}) { /** * 傳入 applyMiddleWare 的函數 * @param {Function} { getState }) [description] * @return {[type]} [description] */ return ({ getState }) => (next) => (action) => { let returnedValue; const logEntry = {}; logEntry.prevState = stateTransformer(getState()); logEntry.action = action; // .... returnedValue = next(action); // .... logEntry.nextState = stateTransformer(getState()); // .... return returnedValue; }; } export default createLogger;
Logger 中這樣的結構 ({ getState }) => (next) => (action) => {}
看起來是很奇怪的,這種設計若是沒有 es6 的箭頭函數,擴展下來就是
/** * getState 能夠返回最新的應用 store 數據 */ function ({getState}) { /** * next 表示執行後續的中間件,中間件有可能有多個 */ return function (next) { /** * 中間件處理函數,參數爲當前執行的 action */ return function (action) {...} } }
這樣的結構本質上就是爲了將 middleware 串聯起來執行,爲了分析 middleware 的執行順序,還得看看 applyMiddleware 的實現
下面是 applyMiddleware 完整的代碼,參數爲 middlewares 數組:
import compose from './compose' /** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner, or logging every action payload. * * See `redux-thunk` package as an example of the Redux middleware. * * Because middleware is potentially asynchronous, this should be the first * store enhancer in the composition chain. * * Note that each middleware will be given the `dispatch` and `getState` functions * as named arguments. * * @param {...Function} middlewares The middleware chain to be applied. * @returns {Function} A store enhancer applying the middleware. */ export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
applyMiddleware
執行事後返回一個閉包函數,目的是將建立 store
的步驟放在這個閉包內執行,這樣 middleware
就能夠共享 store
對象。
middlewares
數組 map 爲新的 middlewares
數組,包含了 middlewareAPI
compose
方法將新的 middlewares
和 store.dispatch
結合起來,生成一個新的 dispatch
方法
返回的 store
新增了一個 dispatch
方法, 這個新的 dispatch
方法是改裝過的 dispatch
,也就是封裝了中間件的執行。
因此關鍵點來到了 compose 方法了,下面來看一下 compose 的設計:
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) }
能夠看到 compose 方法實際上就是利用了 Array.prototype.reduceRight
。若是對 reduceRight 不是很熟悉,來看看下面的一個例子就清晰了:
/** * [description] * @param {[type]} previousValue [前一個項] * @param {[type]} currentValue [當前項] */ [0, 1, 2, 3, 4].reduceRight(function(previousValue, currentValue, index, array) { return previousValue + currentValue; }, 10);
執行結果:
# | previousValue | currentValue | return value |
---|---|---|---|
第一次 | 10 | 4 | 14 |
第二次 | 14 | 3 | 17 |
第三次 | 17 | 2 | 19 |
第四次 | 19 | 1 | 20 |
第五次 | 20 | 0 | 20 |
經過上面的 applyMiddleware 和 中間件的結構,假設應用了以下的中間件: [A, B, C],一個 action 的完整執行流程
一箇中間件的結構爲
function ({getState}) { return function (next) { return function (action) {...} } }
初始化階段一:middlewares map 爲新的 middlewares
chain = middlewares.map(middleware => middleware(middlewareAPI))
執行事後,middleware 變爲了
function (next) { return function (action) {...} }
初始化階段二:compose 新的 dispatch
const newDispatch = compose(newMiddlewares)(store.dispatch)
dispatch 的實現爲 reduceRight, 當一個新的 action 來了事後
/** * 1. 初始值爲: lastMiddleware(store.dispatch) * 2. previousValue: composed * 3. currentValue: currentMiddleware * 4. return value: currentMiddleware(composed) => newComposed */ rest.reduceRight((composed, f) => f(composed), last(...args))
reduceRight 的執行過程:
初始時候
initialValue: composedC = C(store.dispatch) = function C(action) {}
next 閉包: store.dispatch
第一次執行:
previousValue(composed): composedC
currentValue(f): B
return value: composedBC = B(composedC) = function B(action){}
next 閉包 composedC
第二次執行:
previousValue(composed): composedBC
currentValue(f): A
return value: composedABC = A(composedBC) = function A(action){}
next 閉包 composedBC
最後的返回結果爲 composedABC
dispatch(action)
等於 composedABC(action)
等於執行 function A(action) {...}
在函數 A 中執行 next(action)
, 此時 A 中 next
爲 composedBC
,那麼等於執行 composedBC(action)
等於執行 function B(action){...}
在函數 B 中執行 next(action)
, 此時 B 中 next
爲 composedC
,那麼等於執行 composedC(action)
等於執行 function C(action){...}
在函數 C 中執行 next(action)
, 此時 C 中 next
爲 store.dispatch
即 store 原生的 dispatch, 等於執行 store.dispatch(action)
store.dispatch 會執行 reducer 生成最新的 store 數據
全部的 next 執行完事後開始回溯
執行函數 C 中 next 後的代碼
執行函數 B 中 next 後的代碼
執行函數 A 中 next 後的代碼
整個執行 action 的過程爲 A -> B -> C -> dispatch -> C -> B -> A