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 让调用者能够等待。