redux-middleware的实现

前端之家收集整理的这篇文章主要介绍了redux-middleware的实现前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

博客地址

compose 函数开始

函数式编程当中有一个很重要的概念就是函数组合,实际上就是把处理数据的函数像管道一样连接起来,然后让数据穿过管道得到最终的结果。

const add = x => x + 2  // 加
    const minus = x => x - 2    // 减
    const mul = x => x * 2    // 乘
    const div = x => x / 2  // 除

    minus(mul(add(3))) // => 8

如果我想对一个参数执行上面的 add,mul,minus 函数,然后得到返回值,上面的写法可读性就会很差,这时候我们就需要一个 compose 函数,它可以接受上面的函数作为参数,然后返回一个函数,返回的这个函数也可以达成上面的效果,如下

...
    const myOperate1 = compose(minus,add) // 加 => 乘 => 减
    const myOperate2 = compose(div,add,mul) // 乘 => 加 => 除

    myOperate1(3) // => 8
    myOperate2(3) // => 4

关于 compose 很多函数编程库里面都有实现,这里我们不考虑一些特殊情况,对于这个需求自己简单实现一下大概类似这样

const compose = (...args) => args.reduce( (pre,d) => num => pre(d(num)) )

ScriptOJ (一个关于 Web 前端开发评测系统的网站) 上也有这样一道题,可以自己去测试下。

redux 的中间件

我们之前有去手动实现 redux 异步,其实也算有一些简单的中间件,最终目的就是改造了 storedispatch 函数。但是当时自己纯粹为了实现目的,代码可读性和延展性几乎没有。用过 nodejskoa 框架的一定知道中间件这个概念。redux 这里也是使用了中间件的思想,用来增强 reduxdispatch 函数

redux 规定的 middleware 格式

// redux 中间件 middleware 的格式
export default store => next => action => {
    console.log('dispatch: ',action)
    next()
    console.log('finish: ',action)
}

上面的代码我们可以感受很多函数编程的思想(这也是我喜欢 react 一个很重要的原因),函数编程中有一个重要的概念 柯里化,其实我们之前去实现 compose 函数时候细心的同学也能发现,作为 compose 函数的参数,这些函数应该都只接受一个参数,redux 规定的中间件写法是一个链式返回函数的写法,中间的参数都已经固定了,也就是我们只能在最后的代码框里书写中间件的业务逻辑,在这个里面我们可以拿到哪些参数使用呢,很明显就是上面链式返回函数里的参数。

action 我们很容易想到 dispatch 函数的参数就叫做 action,但其实这里拿到的 action 应该是我们原始的内容经过 reduxmiddleware 一层一层处理到当前中间件时候的 action,有了 action 我们要怎么处理它, 我们最原始的 dispatch 函数去哪里了,storenext 又指的是什么呢?带着问题我们继续看他后面是怎么处理的。

中间件各个参数含义

// redux 对 middleware 的处理
...
const store = createStore(...args)
let dispatch = store.dispatch
let chain = []

const middlewareAPI = {
    getState: store.getState,dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
...

这里 middlewares 就是我们创建 store 时候传入的中间件数组,这里先是对 middlewares map 循环传入参数 middlewareAPI 得到返回值。 这里middleware 传入的第一个参数 store 已经找到了,我们可以在 store 上取到 getStatedispatch 这两个函数。那么 next 函数又是传入的呢,这里我们看到了熟悉的 compose 函数了,我们来看看这里 compose 函数的实现。

// 处理中间件的 compose 函数
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)))
}

这里的 funcs 指的就是上面的 chain 数组,这里多了对于中间件个数的判断,最后通过 reduce 的实现已经和我们开篇讲的非常相似了。这里同样是把这些函数功能叠加起来,a 相当于排在前买呢对 middleware,b 则是靠后的 middleware,可以看到 a 执行的参数传入的是 b(...args),所以对于 a 来说它获取到的 next 就是经过 b 处理过的 dispatch

有时候抽象的东西总是难以理解,我们这里假设传入中间件一共有三个【我们熟悉的 thunk,promsie 和 logger】,所以这里执行完效果应该如下

  • logger 而言 next --> dispatch
  • promise 而言 next --> logger 处理 - dispatch
  • thunk 而且 next --> promise 处理 - logger 处理 - dispatch

这里注意 middlewareAPI 中的 dispatch 熟悉并没有简单的就赋值为 dispatch,而是包装成一个函数,当函数调用时去调用 dispatch,而后面 dispatch 会被重新赋值,所以中间件通过参数 store 拿到的 dispatch功能应该等同于包装后的 dispatch.

redux-logger 中有这样一句提示Note: logger must be the last middleware in chain,otherwise it will log thunk and promise,not actual actions,希望你把 logger 这个中间件作为最后一个参数,正是因为放在前面的中间件有可能会改变 action,最后这个最贴近原始 dispatch 位置的中间件拿到的 action 基本都是用户最终想要的实际类型。

思考

学习 react 的过程中,对 函数式编程 有了新的认识,让之前很多只停留在了解层面的知识点有了实际应用的地方。感觉很多概念理解起来会比较抽象,但是仔细研究后又会觉得很优雅。

参考

猜你在找的React相关文章