redux中间件的原理

  • 前言
    react已经出来好久了,其生态圈之庞大,一锅炖不下!各类react-xx,已让咱们不堪重负,github上随便一个demo,引入的模块至少都是五指之数+。看着头疼,嚼之无味……。
    在此建议新学者,能够从基础的核心模块学起,前期不要考虑那些数量繁多的马仔小弟,边学边写,我的感受前期核心要学的流程大体以下:
    React ——> React + redux + React-redux ——> React + redux + React-redux + React-router ——> React + redux + React-redux + React-router ;
    其它的,看状况学习和了解,我也很菜,以上感悟都是针对初学者,但愿能够减小他们在学习过程当中接触过多的东西,而影响学习信心和乐趣。
  • 文档
    React小书(做者从无到有,讲述了React的起源,通俗易懂)Note: 第三阶段的文档如今开始收费查看了,不过对于搞前端的人来讲不用钱也能够来个亲密接触的(你们本身想办法)
    Redux莞式教程(抛开需求讲实用性都是耍流氓,做者扮演一位PM给咱们上了生动的一课,深刻浅出,简明扼要)
    React-Router文档(一部中规中矩的翻译之做)

以上是整理的一些说明和文档资料,没有看过的能够去了解一下。下面将开始本文的主题:redux的中间件applyMiddleware。前端

都说名字越长,越让学者惧怕,applyMiddleware的名字看起来就挺吓人,那么为何会出现中间件,它是作什么的?它为何叫中间件?为何说能够用来解决异步dispatch?通过一段时间的了解,让我渐渐明白了它的工做原理,如今让咱们带问题,怀着简单,轻松的心态走进applyMiddleware大讲堂:react

 

  1. 为何会出现中间件?
    咱们知道redux的核心,就是控制和管理全部的数据输入输出,所以有了dispatch,因为dispatch是一个很纯的纯函数,就是单纯的派发action来更改数据,其功能简单且固定。
    假如如今产品经理A某有个需求,要求记录每次的dispatch记录,咱们怎么办呢?最简单直接的办法就是在每个dispatch的前面加上:
    console.log('dispatching', action);
    dispatch(action)

     假如又来一个产品B说,我须要记录每次数据出错的缘由,咱们怎么办呢?而后咱们又须要在对每个dispatch作修改git

    try{
      dispatch(action)    
    }catch(err){
      console.error('错误报告: ', err)  
    }  

    若是咱们的程序中有不少的dispatch,咱们就须要添加不少的重复代码,虽然编辑器提供批量替换,但这无疑是产生了不少样板代码。
    由于全部的需求都是和dispatch息息相关,因此只要咱们把日志放进dispatch函数里,不就行了吗,咱们只须要更改dispatch函数,把dispatch进行一层封装。
    大概的封装就是下面这样:
    github

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

    Redux把这个封装的入口写成了一个函数,就叫applyMiddleware。
    由此咱们明白了applyMiddleware的功能:改造dispatch函数,产生真假dispatch,而中间件就是运行在假真(dispatchAndLog和next)之间的代码。
    这里咱们要对applyMiddleware进行一个准确的定义,它只是一个用来加工dispatch的工厂,而要加工什么样的dispatch出来,则须要咱们传入对应的中间件函数(好比上例中的dispatchAndLog),下面咱们构造一个精简版的applyMiddleware:redux

    const applyMiddleware = function(middleware){
      let next = store.dispatch;
      store.dispatch = middleware(store)(next);  // 这里传入store,是由于中间件中有可能会用到getState获取数据,好比打印当前用户等需求
    }
    
    applyMiddleware(dispatchAndLog) 

     

  2. 中间件的串联融合。
    中间件的功能各不相同,它们都要融入到dispatch中,在派发action的时候,按照顺序一个个的执行,这是一个费脑经的事情。
    假如如今咱们有两个中间件 logger和collectError两个中间件函数,那么大概的执行顺序就是 dispatch——>logger改造——>collectError改造。这里咱们能看到后面的中间件须要接收到前面改造后的dispatch,
    在前面,咱们是直接修改store.dispatch,如今咱们换一种写法,让每个中间件函数,接收一个dispatch,而后返回一个改造后的dispatch,来做为下一个中间件函数的next,以此类推,用ES6的写法大概代码以下:
    const logger = store => next => action => {
      console.log('dispatching', action)
      return next(action)
    }
    
    const collectError = store => next => action => {
      try {
        return next(action)
      } catch (err) {
        console.error('Error!', err)
      }
    }

     而后,咱们改造一下applyMiddleware,来接收一个middlewares数组:数组

    function applyMiddleware(middlewares) {
      middlewares = middlewares.slice()
      middlewares.reverse()
    
      let dispatch = store.dispatch
      middlewares.forEach(middleware =>
        dispatch = middleware(store)(dispatch)
      )
      return Object.assign({}, store, { dispatch })
    }

     

    上面的middleware(store)(dispatch) 就至关因而 const logger = store => next => {},这就是构造后的dispatch,继续向下传递。这里middlewares.reverse(),进行数组反转的缘由,是最后构造的dispatch,其实是最早执行的。由于在applyMiddleware串联的时候,每一个中间件只是返回一个新的dispatch函数给下一个中间件,实际上这个dispatch并不会执行。只有当咱们在程序中经过store.dispatch(action),真正派发的时候,才会执行。而此时的dispatch是最后一个中间件返回的包装函数。而后依次向前递推执行。
    咱们拿logger和collectError来讲明:

    构造过程:react-router

    let next = store.dispatch;
    let dispatch1 = logger(store)(next); 
    // 这时候的console.log('dispatching', action) 是没有执行的
    
    let dispatch2 = collectError(store)(dispatch1);
    // 这时候的console.log('Error!', err) 也是没有执行的
    
    store.dispatch = dispatch2;

    执行过程:app

    store.dispatch(action); //假如咱们程序中派发了某个action
    
    //至关因而下面这样
    dispatch2(action); //此时执行了 console.log('Error', err)
    
    //因为collectError中间件中的next是接收的logger返回函数即dispatch1,因此在开始执行
    dispatch1(action); //此时执行了 console.log('dispatching', action)
    
    // 这个例子不太合理,由于错误报告是先 try 的 next(action),可是正常的流程是如此。

     

     未完待续……异步

相关文章
相关标签/搜索