前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简析redux技术栈(一):redux中间件

简析redux技术栈(一):redux中间件

作者头像
flytam
发布2020-01-14 17:57:46
5180
发布2020-01-14 17:57:46
举报

一般使用了中间件的 redux 初始化是下面这样的

function configureStore(initialState) {
  return {
    ...createStore(
      reducer,
      initialState,
      applyMiddleware(middleware1, middleware2, middleware3)
    )
  };
}

const store = configureStore({});

redux 中间件是一个函数,形式

const middleware1 = store => next => action => {
  console.log("before", 1);
  next(action);
  console.log("after", 1);
};

const middleware2 = store => next => action => {
  console.log("before", 2);
  next(action);
  console.log("after", 2);
};

const middleware3 = store => next => action => {
  console.log("before", 3);
  next(action);
  console.log("after", 3);
};

需要做到

  • 保证每一个中间件内的 store 引用都是最新的,并且是同一个
  • 连接起来。执行一次 dispatch,会依次执行每一个中间件

代码解释

function applyMiddleWare(...middlerWares) {
  return createStore => (...args) => {
    const store = createStore(...args);
    let dispatch = () => {};
    const middlerWareAPI = {
      dispatch: action => dispatch(action),
      getState: () => store.getState
    };
    const chain = middlerWares.map(middleWare => chain(middlerWareAPI));

    dispatch = chain.reduce((f, g) => (...args) => f(g(...args)))(
      store.dispatch
    );

    return {
      ...store,
      dispatch
    };
  };
}

从上面可以看到,一个中间件函数要最后完成,需要再执行前经过两次的初始化(分别传入 store 和 next 方法),然后到最后的调用

  • 第一次调用。
const chain = middlerWares.map(middleWare => chain(middlerWareAPI));

store两个方法传递给中间件,所有中间件内都是同一份store

  • 第二步调用也是最关键的地方,就是将所有的中间件串联起来,dispatch一次就执行所有的中间件
// 这里其实就是compoose的实现
dispatch = chain.reduce((f, g) => (...args) => f(g(...args)))(store.dispatch);
// 等价于类似 下面的形式
dispatch = middleWare1(middleWare2(middleWare3(dispatch)));

我们知道,经过第一步的初始化,对于middleWare1函数,可见next参数就是指向了middleWare2(dispatch)。只有我们的next调用了才会执行后面的中间件。而到了最后一个中间件middleWare3,它的next参数就是 redux 对应的dispatch函数了。从而最终把我们的action派发到store中去。然后调用栈依次返回。就像一个剥洋葱一样的东西。

所以如果我们把开头的 3 个中间件组合起来运行的话,

输出是

before 1
before 2
before 3
经过reducer // reducer中的log测试
after 3
after 2
after 1

经过上面的代码,我们还可以发现applyMiddleWare其实也是一个高阶函数。applyMiddleware(middleware1, middleware2, middleware3)执行后,是一个接收createStore参数的函数。在createStore里面又是如何操作的。源码:

function createStore(reducer, preloadedState, enhancer) {
// .....enhancer就是上面提到的 applyMiddleware(middleware1, middleware2, middleware3)返回结果
  if (typeof enhancer !== "undefined") {
    return enhancer(createStore)(reducer, preloadedState);
  }
// ....

其实就是把原始的createStore再传进入,进行创建store。返回一个更牛逼的store,这个store其实只是重写了dispatch方法。

通过上面一堆分析,有几个结论了:

1、 中间件内部必须调用next方法。才能调用下一个中间件并且到达 action

2、中间件内部如果调用了dispatch(重写后的)。不加处理就会死循环,相当于这个洋葱剥到中间又开始剥了。

那么问题来了,dispatch什么时候用呢?

这就提到一个大名鼎鼎的库redux-thunk。它的源码里面就是使用了这个dispatch

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;

redux-thunk相信都很熟悉,一般用于处理编写异步逻辑下。它的源码更是牛逼。只有十几行。从上面可以看出它也是一个中间件,它的逻辑就是允许你dispatch一个函数,当你 dispatch 一个函数的时候,就直接执行它,并传入了dispatch(注意这个 是 dispatch 不是 前面中间件提到的 next)和getState方法

const fetchApi = (...args) => (dispatch, getState) => {
  setTimeout(() => {
    dispatch({ type: "xx", payload: "good" });
  }, 7000);
};

dispatch(fetchApi("xxx"));

通过前面的分析,传进thunk内的dispatch参数就是经过包装的dispatch,所以当我们去分发一个同步的普通action的时候,它又能经过我们的其余普通中间件逻辑的处理了

reduxapplyMiddleWare函数和redux-thunk。代码很简短,却隐藏着大智慧

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 通过上面一堆分析,有几个结论了:
  • 那么问题来了,dispatch什么时候用呢?
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档