redux中间件

本文深入解析Redux中间件的工作原理,重点介绍了如何使用thunk和logger中间件改造dispatch函数,实现异步action和state变更记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

redux的中间件是在发出 Action之后,执行 Reducer 之前,添加的一些实现其他功能的函数。中间件的主要作用就是改造dispatch函数,使我们在发起action的时候可以实现一些其他业务逻辑。下面加入中间件创建一个store:

const logger = createLogger();
const store = createStore(
    rootReducer,
    initialStore,
    applyMiddleware(thunk, logger),
);

在项目中一般会用到thunk和logger中间件,thunk的作用是可以发起异步action,logger的作用是在改变state前后打印state。创建store时通过调用applyMiddleware可以将两个中间件串起来依次执行。
createStore(reducer, preloadedState, enhancer),createStore的第三个函数是高阶函数,如果传了会返回enhancer(createStore)(reducer, preloadedState)。所以下面这种写法和上面相同:

const store = applyMiddleware(thunk,logger)(createStore)(rootReducer, initialStore);

接下来看一下具体是怎么使用中间件创建一个store的。

applyMiddleware

首先来看一下applyMiddleware,这个函数的作用是将所有中间件组成一个数组,依次执行。源码如下:

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
    }
  }
}

对应的ES5的源码如下,可能会比较容易理解

function applyMiddleware() {
  for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  return function (createStore) {
    return function () {
      var store = createStore.apply(void 0, arguments);

      var _dispatch = function dispatch() {
        throw new Error("Dispatching while constructing your middleware is not allowed. " + "Other middleware would not be applied to this dispatch.");
      };

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch() {
          return _dispatch.apply(void 0, arguments);
        }
      };
      var chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = compose.apply(void 0, chain)(store.dispatch);
      return _objectSpread({}, store, {
        dispatch: _dispatch
      });
    };
  };
}
const store = applyMiddleware(thunk)(createStore)(rootReducer, initialStore) 

调用 applyMiddleware函数创建store的时候先传了一个中间件thunk,执行applyMiddleware(thunk)返回一个函数,这个函数需要一个参数createStore,接着我们执行applyMiddleware(thunk)(createStore) 将createStore传进去,然后又返回了一个函数,我们传了参数(rootReducer, initialStore)。

参数都传进去之后看一下执行过程:
首先调用createStore(rootReducer, initialStore)创建了一个store,然后定义了一个dispatch(对应的ES5写法中的_dispatch)方法,函数做了一个错误处理。然后定义了一个middlewareAPI对象,里面有一个getState函数将store.getState赋给它,还有一个dispatch函数,调用时返回的是我们刚才定义的dispatch(_dispatch)函数。
然后执行

const chain = middlewares.map(middleware => middleware(middlewareAPI))

在调用applyMiddleware的时候只传了一个参数thunk进去,所以

const chain = [thunk(middlewareAPI)]

然后执行

dispatch = compose(...chain)(store.dispatch)

compose的作用是从右到左组合多个函数,比如: compose(f1,f2,f3) = (args) => f1(f2(f3(args))。因为这里只有一个函数,所以

dispatch = thunk(middlewareAPI)(store.dispatch)

最后返回改造过的dispatch方法。

 return {
   ...store,
   dispatch
 }

redux-thunk

刚才的dispatch方法中用到了thunk中间件:

 dispatch = thunk(middlewareAPI)(store.dispatch)

thunk是一个让dispatch可以处理异步action的中间件。
首先看一下redux-thunk的源码:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

从上面的代码可以看出

thunk = createThunkMiddleware() = 
({ dispatch, getState }) => next => action => {
  if (typeof action === 'function') {
    return action(dispatch, getState, extraArgument);
  }

  return next(action);
};

thunk(middlewareAPI)(store.dispatch)在调用时首先传入了middlewareAPI,即传了({ dispatch, getState })。执行thunk(middlewareAPI)后,返回一个函数,这个函数需要一个参数next,对应传入的参数就是store.dispatch,然后又返回一个函数,所以

 dispatch = action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next(action);
 };

在项目中调用dispatch的时候会传一个action进去,如果传入的是函数,就执行这个函数,否则就执行next(action),即 store.dispatch(action)。
比如我传了下面这个action函数

function fetchHomeData(args) {
  return (dispatch) => {
    getHomeData(args).then((res) => {
      dispatch({
        type: actionTypes.FETCH_HOME_DATA,
        homeData: res.data,
      });
    })
  };
}

//调用
dispatch(fetchHomeData(args));

因为传入的是一个函数,会执行该函数;

dispatch(fetchHomeData(args)) = fetchHomeData(args)(dispatch, getState, extraArgument);

可以看到请求数据之后会再次发起一个dispatch

dispatch({
  type: types.FETCH_HOME_DATA,
  homeData: res.data,
});

这个dispatch对应的是applyMiddleware里面的dispatch方法,。

 dispatch: (...args) => dispatch(...args)

// es5代码
dispatch: function dispatch() {
  return _dispatch.apply(void 0, arguments);
}

在applyMiddleware函数里面又创建了一个dispatch函数,形成了一个闭包,所以在调用时返回的dispatch方法和最后通过中间件改变后的dispatch方法相同。

dispatch = compose(...chain)(store.dispatch)

// es5代码 上面的_dispatch和这个_dispatch相同
_dispatch = compose.apply(void 0, chain)(store.dispatch);

再次发起dispatch的时候又会调用一次thunk,这次传的action是一个对象,所以会执行next(action), 即直接执行store.dispatch(action),这次发起的dispatch是store中最原始的dispatch函数。

增加logger中间件

在一开始创建store的时候除了thunk中间件,还加入了logger中间件,用来打印state。

const store = applyMiddleware(thunk, logger)(createStore)(rootReducer, initialStore)

前面执行过程都是一样的,执行到这一行代码的时候有点不同

const chain = middlewares.map(middleware => middleware(middlewareAPI))

执行之后

const chain = [thunk(middlewareAPI), logger(middlewareAPI)]

接下来执行dispatch = compose(…chain)(store.dispatch),compose中会先执行logger(middlewareAPI)(store.dispatch),将执行的结果作为thunk(middlewareAPI)的参数再执行,得到:

dispatch = thunk(middlewareAPI)(logger(middlewareAPI)(store.dispatch))

先执行thunk函数,action还是传入之前的fetchHomeData函数,传入的是一个函数,所以会执行fetchHomeData函数。执行时又会发起一个dispatch,传入了一个action对象,接下来会执行next(action),这次对应的next是下一个中间件。所以会执行

logger(middlewareAPI)(store.dispatch)(action)

简化后的logger代码是这样的:

const logger = ({ getState }) => (next) => (action) => {
  // ...打印当前state
  returnedValue = next(action);  // next(action) = store.dispatch(action)
  // ...打印改变之后的state
  return returnedValue;
};

在执行 next(action)的时候执行了redux原始的store.dispatch函数。

加入中间件后,最后都是会执行store.dispatch(action),只是改造的dispatch方法在执行过程中会加入一些其他作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值