看图读懂 redux applyMiddleware 基本原理

一、前言

最近在学习 redux,由于前面有过 Vuex 使用经验,所以觉得 redux 的使用方式何其也大致相同,结果...,是我想多了,这里不得不说对于我们这种后端思维来说,还是 Vue 比较容易接受一些,当然 react 也很优秀!好了,闲话不多说,接下来就进入正题。

二、redux 中的 middleWare 

这里笔者也就过多花时间介绍 redux 了,我看的教程是是这个【Redux】,文中基础部分都讲得比较详细,看明白还是没有什么问题,但是其中高级部分讲解到的 redux middleware 概念的时候,其本身意义以及作用还是比较好理解!但是在文中详细的介绍了如何从一个简单的 middleware 实现到如果实现讲一个闭包 middleware 结构按照顺序安装到 redux 的时候,的确比较难理解,其中笔者觉得最难理解的当属文中提到的 applyMiddleware (一个“单纯”的为安装使用 middleware 的实现),代码如下:

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

middleware 的形式如下:

// 普通函数
function logger(store) {
  return function wrapDispatchToAddLogging(next) {
    return function dispatchAndLog(action) {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
  }
}

// 使用 “箭头函数”替代,实现“柯里化 ”(读者自行了解)
// middleware 功能: 日志打印器
const logger = store => next => action => {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

// middleware 功能: 崩溃信息收集
const crashReporter = store => next => action => {
  try {
    return next(action)
  } catch (err) {
    console.error('Caught an exception!', err)
    // 如下代码需 raven-js 依赖, 这里为了演示效果注释掉.
    /*
    Raven.captureException(err, {
      extra: {
        action,
        state: store.getState()
      }
    })
    */
    throw err
  }
}

 

使用方式如下:

/**
 * 假设如下代码模拟 redux 的实际使用
 */

const ACTION_TYPES = {
    NORMAL_ACTION : 'NORMAL',
    EXCEPTION_ACTION : 'EXCEPTION'
}

const store = {
    dispatch(action) {
        const { NORMAL_ACTION, EXCEPTION_ACTION } = ACTION_TYPES;
        
        if(action.type === NORMAL_ACTION) {
            console.log('normal running...');    
        }

        if(action.type === EXCEPTION_ACTION) {
            console.log('exception running');
            let i = 1 / 0; // 这里将会抛出异常
        }
    },

    getState() {
       return 'this is a foo state'
    }
}

const storeEnforce = applyMiddleware(store, [logger, crashReporter]);
storeEnforce.dispatch({ type : ACTION_TYPES.NORMAL_ACTION});
storeEnforce.dispatch({ type : ACTION_TYPES.EXCEPTION_ACTION});

将如上的代码组合之后输出的结果如下:

normal running...
index4.html:52 next state this is a foo state
index4.html:50 dispatching {type: "EXCEPTION"}type: "EXCEPTION"__proto__: Object
index4.html:101 exception running
index4.html:52 next state this is a foo state

logger 和 crashReport 通过 applyMiddleware 几行精简的代码就被顺序的安装在在了 store.dispatch 执行之前,并且按照传入的顺序被顺序安装,那下面我们就来通过画图的方式理解 applyMiddleware 是如何实现上述的操作的。

上述的 applyMiddleware 可以中包含如下代码:


  let dispatch = store.dispatch

  middlewares.forEach(middleware =>
    dispatch = middleware(store)(dispatch)
  )

这段代码很不容易理解,我们接下来将其拆分成如下形式:


 middlewares.reverse(); // 反转数组 
 let dispatch = store.dispatch

  middlewares.forEach(middleware =>
    // 第一次返回一个 function, 只要 tempFun 还能够被访问, 则作为参数传递进去的
    // store 就不会被释放,这是 JS 中的闭包特性,读者可以自行了解 JS 闭包相关知识。
    let tempFun = middleware(store)
    // 由于 tempFun 依然是一个函数, 其接收一个“函数”,再返回一个包含“接收的参数(类型:函数)”的
    // 函数(返回结果类型),又形成了一个闭包。
    let previousCallback = tempFun(dispatch)
    // 最后使 dispatch 指向新的 previousCallback, 二旧的 dispatch 已经保存在了
    // previousCallback 这个闭包结构中
    dispatch = previousCallback 
  )

通过上面的拆分可以中,middlewares 中的每个 middleware(嵌套闭包结构) 不断的被用来保存下一个 middleware 创造的闭包结构,因此在执行完成之后,其实循环完成之后 dispatch 是指向第一个 middleware 创造的闭包函数,然后 middleware 中接收的第二个参数 next 其实是指向下一个 middleware 创造的闭包函数,当然,最后一个 middleware 的 next 指向的是真实的dispatch, 这样,每个 middleware 中再调用 next, 相当于串联的在执行真实的 dispatch 之前把所有的 middleware 都调用了一遍,而我们只需要像平常一样调用 store.dispatch({...}) 就可以了,是不是很像“多米诺骨牌”。

是不是还有些不明白,接下来我们画图展示上述的过程:

applyMiddleware 过程图示

好了,上图应该很清楚了,相信读者结合上图理解了 applyMiddleware 组装 middleware 原理之后,再去参看 react-redux 的 applyMiddleware 的源码将会很容易理解。

三、总结

通过 applyMiddleware 的学习,使我知道了 JS 的闭包还能有如此巧妙而且优雅的使用方式,同时也算学习了新知识,嗯,继续加油!!!

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值