redux中间件浅析

redux中间件的做用

在redux的流程中,会存在一些特殊的需求,好比打印action的信息来进行调试,或者要处理一个异步请求。中间件就是为了处理这些特殊的需求而存在的。在redux的流程中,action creator 和 reducer 都是纯函数,action creator返回一个包含type和payload的对象,reducer函数用来处理和返回state,应该保证他们的单一性和纯度。因此可以处理这些特殊需求的只剩下dispatch了,redux的中间件就是用来对dispatch进行加强的。redux

一个简单的初始化

import { createStore, applyMiddleware } from 'redux';
  import thunk from 'redux-thunk';
  import promise from 'redux-promise';
  import logger from 'redux-logger';
  import rootReducer from '../reducers';
  ​
  export default function configureStore() {
    return createStore(
      rootReducer,
      applyMiddleware(thunk, promise, logger),
    );
  }复制代码

在使用createStore进行初始化的时候,若是第三个参数不为空,或者第二参数为一个函数,那么就用这个enhancer函数进行初始化。createStore会被当成enhancer函数的参数传入,并在enhancer这个函数再次使用createStore对redux进行初始化。数组

applyMiddleware.js

import compose from './compose';
  ​
  export default function applyMiddleware(...middlewares) {
    return createStore => (...args) => {
      const store = createStore(...args)
      let dispatch = () => {
        throw new Error(
          `Dispatching while constructing your middleware is not allowed. ` +
            `Other middleware would not be applied to this dispatch.`
        )
      }
  ​
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args)
      }
      const chain = middlewares.map(middleware => middleware(middlewareAPI))
      dispatch = compose(...chain)(store.dispatch)
  ​
      return {
        ...store,
        dispatch
      }
    }
  }复制代码

createStore是初始化传进来的,…args是初始化用的reducers和preloadedState,最终生成store对象。promise

在函数内部定义一个dispatch函数,定义一个middlewareAPI对象,将原生的getState和自定义的dispatch赋值给它。bash

middlewares.map将middlewareAPI这个对象送到每一个中间件中,使得每一个中间件都能经过getState访问到整个store上的数据,而且经过闭包将自定义的dispatch函数传到每一个中间件中,使得最终增强过的dispatch函数能被每一个中间件调用。闭包

compose函数用来对中间件数组chain进行处理,生成最终的dispatch函数。app

compose.js

export default function compose(...funcs) {
    if (funcs.length === 0) {
      return arg => arg
    }
  ​
    if (funcs.length === 1) {
      return funcs[0]
    }
  ​
    return funcs.reduce((a, b) => (...args) => a(b(...args)))
  }复制代码

上面就是compose函数,经过数组的reduce函数,将中间件进行合并。异步

compose([f, g, h]) ===> (...args) => f(g(h(...args)))函数

compose函数能够简化为上面的函数,在本文中,引入了三个中间件,下面将用这三个中间件进行简单的说明。ui

const thunk = thunk(middlewareAPI);
  const promise = promise(middlewareAPI);
  const logger = logger(middlewareAPI);
  ​
  const chain = [thunk, promise, logger];
  ​
  compose(...chain)
     ↓ ↓ ↓ ↓ ↓ ↓
  (...args) => thunk(promise(logger(...args)))

  ​
  中间件中的next参数,用来将action传递到下一个中间件中,经过上面的转换能够看出来:
  thunk函数中的next等于promise(logger(..args))
  promise函数中的next等于logger(...args)

  ​
  最终生成的dispatch函数能够表示为:
  dispatch = compose(...chain)(store.dispatch) 
    ↓ ↓ ↓ ↓ ↓ ↓
  dispatch = thunk(promise(logger(store.dispatch)))

  ​
  由于闭包的缘由,最终生成的dispatch函数将会传入到每一个中间件中。复制代码

经过上面的分析,能够看出每个middleware中store的dispatch函数经过闭包的做用,都和compose函数最终生成的dispatch保持一致,而且原生的dispatch函数被包裹在最内层,middleware经过调用next()函数进入下一个中间件,并最终调用原生的dispatch函数实现action的分发,具体的过程以下图this



若是在某一个中间件中调用了store.dispatch,那么就至关于从当前的中间件中跳出,从新执行一遍上图中的过程。若是中间件一直粗暴的执行store.dispatch而没有一个合适的状态去调用next函数,那么就会进入一个死循环中。具体的过程以下图


以reudx-promise为例

import isPromise from 'is-promise';
  import { isFSA } from 'flux-standard-action';
  ​
  export default function promiseMiddleware({ dispatch }) {
    return next => action => {
      if (!isFSA(action)) {
        return isPromise(action) ? action.then(dispatch) : next(action);
      }
  ​
      return isPromise(action.payload)
        ? action.payload
            .then(result => dispatch({ ...action, payload: result }))
            .catch(error => {
              dispatch({ ...action, payload: error, error: true });
              return Promise.reject(error);
            })
        : next(action);
    };
  }复制代码

在redux-promise中,若是action的payload是一个promise对象,那么会在then中调用dispatch方法,此时就会跳出promise这个中间件,从最外层从新进入这个中间件链;若是action的payload是一个正常的对象,那么会执行next函数,进入下一个中间件。

相关文章
相关标签/搜索