Redux做爲目前最火的Flux
模式實現之一,它有不少的點值得研究。今天咱們首先來看看它的Middleware。express
熟悉Express
或者koa
的朋友對Middleware的概念必定不陌生。例如Express中是這樣使用一箇中間件的:編程
var app = express(); app.use(function(req, res, next) { console.log('%s %s', req.method, req.url); next(); });
app.use
中的方法,能夠在其後面的http VERB
調用以前,對request
對象和response
對象進行處理,而後經過調用next
方法將處理過程轉發到下一中間件或者經過返回響應來結束處理過程。(以後有機會的話再寫一寫Node
和Express
)。redux
我理解的所謂中間件其實就是,經過相似裝飾者模式的形式,用代碼預處理的方式,保證本來處理問題的函數(或方法)調用不變。promise
Redux中的中間件可使得在用戶調用store.dispatch
以後,先對參數state
和actions
進行預處理,再讓真正的store.dispatch
調用,以確保reducer
的純度(函數式編程的概念)不變。app
Redux中提供了applyMiddleware
方法,它的源碼只有十幾行,真的是很是精妙。框架
下面咱們就研究一下它的源代碼。koa
<!--more-->異步
applyMiddleware(...middlewares){ return next => (reducer, initialState){ var store = next(reducer, initialState), dispatch = store.dispatch, chain = [], middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }; chain = middlewares.map(middleware => middleware(middlewareAPI)); dispatch = compose(...chain, store.dispatch); return { ...store, dispatch } } }
這段代碼的意思就是,appleMiddleware
方法接收一個Middleware
列表,以applyMiddleware(middleware1, middleware2, middleware3)
的形式調用(參見ES6的rest參數語法),而後再將建立store的方法傳入(我想這個緣由是Redux不單單能夠在React中使用,也能夠適用於任何Flux模式的框架和庫),而後就會發生神奇的事情。函數式編程
這兩次調用(假設:var newCreateSore = applyMiddleware(middleware1, middleware2)(createStore)
)會產生一個新的建立Store的方法,可是它改造了本來Store的dispatch
方法,讓這個dispatch
能夠作原生dispatch
不能作的事情,這樣咱們就能夠訂製dispatch
的行爲,從而實現了中間件的概念。函數
故而,newCreateStore
將做爲createStore
的替代方法,使用newCreateStore
會產生帶有中間件的store。
在最內層是如何實現中間件的調用的呢?讓咱們繼續研究。
首先咱們用傳入的next
(一個能夠建立Store的函數),建立一個原始的store,而且取出其原生的store.dispatch
方法和store.getState
方法成爲一個對象,做爲參數傳入中間件函數中,讓其第一次包裝這個相似store的對象,並返回新的函數。
而後咱們使用compose
函數,將這些包裝事後的返回的函數一個接一個的嵌套調用。
這裏補充一下compose的概念:
假設有若干函數f1, f2, f3...,compose指的是相似f1(f2(f3(x)))的調用方式,這在函數式編程中很常見。
(這裏的compose
函數是redux中的一個方法,這裏咱們不上它的源碼,有興趣的朋友能夠直接看源碼。)
被嵌套在compose最內層的是原生的store.dispatch
方法,這裏咱們就一層層的將其包裝,在中間件函數中,咱們能夠利用store的其餘方法,好比store.dispatch
和store.getState
,作一些有意思的事情,好比實現一個記錄state
改變的日誌中間件。
從上面的分析中,咱們不難寫一個符合要求的中間件函數。
首先中間件函數須要接受一個middlewareAPI
,若是使用ES6的語法,這裏能夠當作是接收一個{dispatch, getState}
的形式的參數,這樣咱們就能在內層使用這兩個方法。
接收middlewareAPI
參數以後,中間件函數返回另外一個函數(爲方便後面解釋,假設返回的函數爲dispatch n
)。這個函數既然要用於compose,也就是說它接收一個形式爲dispatch
的函數,對其一層層嵌套(形式爲dispatch1(dispatch2(dispatch3(dispatch)))
)。在其內部咱們能夠在以前的dispatch
調用以前和以後,進行一些邏輯的處理。
寫一個簡單的記錄state日誌的中間件以下:
var middlewareLogger = ({getState}) => next => action => { console.log(getState()); next(action); console.log(getState()); }
怎麼樣,是否是特別簡單?
再寫一個異步操做的中間件:
const readyStatePromise = store => next => action => { if (!action.promise) { return next(action) } function makeAction(ready, data) { let newAction = Object.assign({}, action, { ready }, data) delete newAction.promise return newAction } next(makeAction(false)) return action.promise.then( result => next(makeAction(true, { result })), error => next(makeAction(true, { error })) ) }
這個中間件讓你能夠發起帶有一個 { promise } 屬性的特殊 action。這個 middleware 會在開始時發起一個 action,並在這個 promise
resolve 時發起另外一個成功(或失敗)的 action。爲了方便起見,dispatch
會返回這個 promise 讓調用者能夠等待。