• 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大讲堂:

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

       假如又来一个产品B说,我需要记录每次数据出错的原因,我们怎么办呢?然后我们又需要在对每一个dispatch做修改

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

      如果我们的程序中有很多的dispatch,我们就需要添加很多的重复代码,虽然编辑器提供批量替换,但这无疑是产生了很多样板代码。
      因为所有的需求都是和dispatch息息相关,所以只要我们把日志放进dispatch函数里,不就好了吗,我们只需要更改dispatch函数,把dispatch进行一层封装。
      大概的封装就是下面这样:

      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:

      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来说明:

      构造过程:

      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;

      执行过程:

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

       未完待续……

  • 相关阅读:
    使用jenkins发布docker容器
    使用jenkins构建和部署前端(jenkins是k8s集群中搭建)
    kubernetes的ingress-nginx使用案例
    使用jenkins的SSH Publishers远程执行
    kubernetes集群:向pod里的/etc/hosts添加解析
    kubernetes集群:nacos搭建
    JavaBean的概念
    Statement与PreparedStatement的区别
    JDBC的概念&使用步骤
    session的用法
  • 原文地址:https://www.cnblogs.com/wshiqtb/p/7909770.html
Copyright © 2020-2023  润新知