彻底理解redux的中间件原理

2,987 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

问题

对于中间件的词middleware的概念并不陌生,相对于 Express 或者 Koa 的 middleware,Redux middleware 被用于解决不同的问题,但其中的概念是类似的。它提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。 你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。

中间件从何而来,为什么要使用中间件,他的核心思想是什么,等一系列面试问题接踵而来。

我们常在使用的中间件,如redux-thunkredux-promiseredux-saga等处理异步的中间件它们是如何被加入到redux生命周期里去的,他们是如何增强 state/dispatch 的,等这些都归功于redux的一个api叫applyMiddleware

1. 为什么要使用中间件

从redux官方可以得知,redux团队以记录日志和创建崩溃报告为例,手动记录日志开始的7个案例,从崩溃到解决,最终促使middleware的诞生

2. 中间件做了什么

核心就是在 action 被发起之后,到达 reducer 之前的一些扩展
主要增强store.dispatch的能力

3. 核心思想

实现核心compose组合执行中间件
组合函数compose是借助于es6的Array.prototype.reduce方法实现

/**
 * compose源码
 */
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  // a为上次中间件执行函数在外层,b为当前中间件函数在里层,也就实现从右往左执行
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

参数...funcs即为中间件函数,如果没有返回空函数v=>v,如果有一个返回第一个中间件函数,如果有多个则返回层级嵌套中间件函数,从右往左执行,右边函数的执行结果作为左边函数的执行参数

applyMiddleware 实现

首先从调用入口开始 createStore.js 部分

/**
 * createStore部分源码
 */
export default function createStore(reducer, preloadedState, enhancer) {
  
  // 省略一些参数类型判断

  // 此处判断将enhancer挪位,可以实现将enhancer参数放在第二个位子
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // 关键代码
    return enhancer(createStore)(reducer, preloadedState)
  }
 }

关键代码解读:

  1. enhancer即为中间件函数 如:applyMiddleware(thunk, logger)
    由上文中间件组合函数compose得知applyMiddleware(thunk, logger)的执行结果为a(b(...args))的组合函数的形式

  2. 第二步传入createStore作为中间件组合函数的参数并执行

  3. 第三步即为使用(中间件增强后的...store/dispach方法)来解析reducer

进入applyMiddleware部分

/**
 * applyMiddleware源码
 */
export default function applyMiddleware(...middlewares) {

  // createStore即为上文第2步传入的参数
  return createStore => (...args) => {
  
    // 执行createStore生成store
    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)
    }
    
    // 将每个中间件带上 middlewareAPI 参数并返回新数组
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    
    // 执行compose中间件组合函数(将store.dispatch作为参数)
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

结语

任何框架或库的中间件都是为了提供给用户一个可配置执行额外逻辑代码的勾子,是一种enhancer
看懂了redux中间件的原理,学习其他框架的中间件可更为轻松!

往期精彩文章

🌟canvas实现刮刮奖效果
🌟前端实现pdf下载
🌟web前端性能优化(全汇总)
🌟一句话概括this指向问题
🌟MutationObserver 实现微任务原理分析
🌟遇到几次的大厂笔试题:装饰数组push方法
🌟V8垃圾回收策略与GC算法
🌟浏览器缓存策略(强缓存和协商缓存)
🌟$nextTick 源码解读与原理分析
🌟手动封装适合react hook使用的状态管理工具