专栏首页黄腾霄的博客2020-6-17-从0开始实现redux中间件机制

2020-6-17-从0开始实现redux中间件机制

今天和大家聊一聊redux的中间件原理。

注:本文内容大部分参考redux的官网文档Middleware - Redux。如果英文好的同学可以直接阅读官网文档,写的非常好。


关于redux-middleware

redux中间件提供了一个切面的关注点。

我们可以很方便的利用中间件进行AOP编程,比如日志功能,埋点上报等。

这里主要是利用装饰器模式,在实际任务执行之前,动态添加before和after的逻辑。

这样就能形成一个洋葱模型

接下来我们就看下如何手动开写一个日志中间件

手动处理日志

首先我们手动处理日志,就是在执行dispatch方法前后,添加console.log

console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())

抽取方法

手动处理日志有一点问题,假如我们有多处执行action的地方需要用到日志,我们不可能每一处都进行复制粘贴。

那么这时候就需要额外抽取一个方法。

function dispatchAndLog(store, action) {
  console.log('dispatching', action)
  store.dispatch(action)
  console.log('next state', store.getState())
}

这样我们只要对原来执行dispatch的地方替换为这个方法,就可以实现日志功能了

dispatchAndLog(store, addTodo('Use Redux'))

Monkeypatching

上一层的方法虽然解决了重复代码的问题,但是还是需要我们修改所有的dispatch处。

这就会导致入侵业务代码。

那有没有非入侵的方式呢?有,就是Monkeypatching。

Monkeypatching,简单的来说就是用自己定义的新方法,替换对象的原始方法。

这样虽然业务代码(使用方)没有进行改动,但是实际的执行代码已经在运行时被更改了。

const next = store.dispatch
store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

处理多中间件问题

上一步我们解决了一个中间件的问题,假如我们现在需要添加一个新的中间件,那么应该怎么处理呢?

最简单的方法是复制一遍逻辑。

let next = store.dispatch
store.dispatch = function dispatchAndLog1(action) {
  console.log('dispatching1', action)
  let result = next(action)
  console.log('next state1', store.getState())
  return result
}

next = store.dispatch
store.dispatch = function dispatchAndLog2(action) {
  console.log('dispatching2', action)
  let result = next(action)
  console.log('next state2', store.getState())
  return result
}

显然,获取next和赋值store.dispatch都是重复逻辑,应该抽取出来成为公共代码.

假如我们做如下改动,直接返回包装后的函数会如何?

function logger(store) {
  const next = store.dispatch

  return function dispatchAndLog(action) {
    console.log('dispatching', action)
    let result = next(action)
    console.log('next state', store.getState())
    return result
  }
}

这样我们就可以使用一个foreach对一个中间件数组进行添加。

function applyMiddlewareByMonkeypatching(store, middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()

  // Transform dispatch function with each middleware.
  middlewares.forEach(middleware => (store.dispatch = middleware(store)))
}

OK,这个解决方案已经很棒了,但是我们还可以更进一步。

试想一下,我们在中间件代码中,其实并不关心next方法是不是store.dispatch,只需要知道它能够链式处理action即可。

那么我们可以进一步隐藏这个概念。

const logger = store => next => action => {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}
function applyMiddleware(store, middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()
  let dispatch = store.dispatch
  middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch)))
  return Object.assign({}, store, { dispatch })
}

这样一来,我们在执行时,只要对一个dispatch中间变量进行处理,最后再赋值在store即可。

小结

在这里我们看到了如何一步步实现一个redux的中间件机制。

实际上,类似express,koa等后端框架的中间件机制也是用类似的方法进行处理的。

有了中间件,我们可以更方便的在非入侵业务代码的情况下实现更多复杂的功能。


参考文档:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 2019-12-1-实现一种异步版本的ManualResetEvent

    我们在2019-12-1-使用SemaphoreSlim实现异步等待 - huangtengxiao给大家介绍了信号量的异步等待使用方法。可惜的是.NET的Ma...

    黄腾霄
  • 2019-1-30-wcf入门(10)

    在上一篇博客中介绍了2019-1-29-wcf入门(9) - huangtengxiao如何使用错误协定回馈指定异常,但是往往会存在部分异常是非预期的。这一篇会...

    黄腾霄
  • 2019-12-1-实现一种异步版本的AutoResetEvent

    我们在2019-12-1-实现一种异步版本的ManualResetEvent - huangtengxiao给大家介绍了异步版本的ManualResetEven...

    黄腾霄
  • 单基因套路看腻了?换个套路不一样的风景

    Identification of immune-enhanced molecular subtype associated with BRCA1 mutati...

    科研菌
  • c语言函数指针的理解与使用

    C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型。 B) 也很简单,与C)表达式相比,唯一不同的就是函数...

    用户7678152
  • 1.6 VR扫描:Snap收购图像识别公司AI Factory;任天堂正研究采用AR的新方式

    近日,市场研究机构SuperData发布《2019年数字游戏和互动媒体产业报告》。数据显示,2019年数字游戏产业和互动媒体产业规模同比增长4%达到1201亿美...

    VRPinea
  • c语言函数指针的理解与使用

    C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型。 B) 也很简单,与C)表达式相比,唯一不同的就是函数...

    用户7678152
  • 关于quartus ii 11.0系列&dsp builder 11.0&matlab R2011b&synplify 9.6.2的下载与安装全解

    查找,下载,安装了几天FPGA相关处理软件,有点经验教训要和大家分享一下,希望后来人不用走那么多弯路。首先, 在安装DSP Builder之前,首先安装Ma...

    s1mba
  • C语言 python Java 等主要流行编程语言优劣对比

    如果一个IT小白准备进入IT开发行列,是该选Java呢还是另外的开发语言呢?开发一直是一个倍受争议的话题,那么其实这个问题并没有标准答案。 分享之前我还是要推荐...

    企鹅号小编

扫码关注云+社区

领取腾讯云代金券