浅谈Redux(之一):Middleware原理

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方法将处理过程转发到下一中间件或者经过返回响应来结束处理过程。(以后有机会的话再写一写NodeExpress)。redux

我理解的所谓中间件其实就是,经过相似装饰者模式的形式,用代码预处理的方式,保证本来处理问题的函数(或方法)调用不变。promise

Redux中的中间件可使得在用户调用store.dispatch以后,先对参数stateactions进行预处理,再让真正的store.dispatch调用,以确保reducer纯度(函数式编程的概念)不变。app

Redux中提供了applyMiddleware方法,它的源码只有十几行,真的是很是精妙。框架

下面咱们就研究一下它的源代码。koa

<!--more-->异步

applyMiddleware方法

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

结束

相关文章
相关标签/搜索