zoukankan      html  css  js  c++  java
  • redux进阶 --- 中间件和异步操作

      你为什么需要异步操作? https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux  

      在redux基础篇的介绍中,我们介绍了redux的基本概念, 对于state的改变有了详尽的了解,但是并没有提到异步问题如何解决? 何为异步? Action 发出以后,Reducer 立即算出 State,这叫做同步;Action 发出以后,过一段时间再执行 Reducer,这就是异步。 

      在vue中对异步的实现是在actions下使用,如下所示:

     getDefaultAddress ({commit, state}) {
          return new Promise(function (resolve, reject) {
            axios.get('/bbg/user/get_default_address', {
              params: {
                uid: localStorage.getItem("uid")
              }
            }).then(function (response) {
              if (response.data.code == 152) {
                console.log("获取默认地址成功");
                // 存储默认地址
                commit(UPDATE_DEFAULT_ADDRESS, response.data.data);
              }
              resolve();
            });
          });
        },

      这里是一个异步的操作,即首先执行getDefaultAddress, 然后在执行这个的过程中,我们需要等到返回结果之后再去改变state数据,而vue的方法就是在判断成功的使用commit一个reducer,这样,就可以完成异步的操作了。 

      那么react中是如何实现这种异步操作呢?  这时就需要使用工具: 中间件了。 

      

    一、中间件的概念

      为了理解中间件,让我们站在框架作者的角度思考问题:如果要添加功能,你会在哪个环节添加?

    1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
    (2)View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。
    (3)Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。

      想来想去,只有发送 Action 的这个步骤,即store.dispatch()方法,可以添加功能。举例来说,要添加日志功能,把 Action 和 State 打印出来,可以对store.dispatch进行如下改造。

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

      

      上面代码中,store.dispatch进行了重定义在发送 Action 前后添加了打印功能。这就是中间件的雏形

      中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。

    个人理解:

    将具体业务和底层逻辑解耦的组件。

    大致的效果是:
    需要利用服务的人(前端写业务的),不需要知道底层逻辑(提供服务的)的具体实现,只要拿着中间件结果来用就好了。

    举个例子:
    我开了一家炸鸡店(业务端),然而周边有太多屠鸡场(底层),为了成本我肯定想一个个比价,再综合质量挑选一家屠鸡场合作(适配不同底层逻辑)。由于市场变化,合作一段时间后,或许性价比最高的屠鸡场就不是我最开始选的了,我又要重新和另一家屠鸡场合作,进货方式、交易方式等等全都要重来一套(重新适配)。

    然而我只想好好做炸鸡,有性价比高的肉送来就行。于是我找到了一个专门整合屠鸡场资源的第三方代理(中间件),跟他谈好价格和质量后(统一接口),从今天开始,我就只需要给代理钱,然后拿肉就行。代理负责保证肉的质量,至于如何根据实际性价比,选择不同的屠鸡场,那就是代理做的事了。

      即中间件实际上就是在某两个步骤之间承担一部分任务,完成某个功能,这就是中间件。 

      

    二、 中间件的用法

      本教程不涉及如何编写中间件,因为常用的中间件都有现成的,只要引用别人写好的模块即可。比如,上一节的日志中间件,就有现成的redux-logger模块。这里只介绍怎么使用中间件

      

    import { applyMiddleware, createStore } from 'redux';
    import createLogger from 'redux-logger';
    const logger = createLogger();
    
    const store = createStore(
      reducer,
      applyMiddleware(logger)
    );

      

      即先从redux中import使用中间件的插件,然后就可以在创建store的时候使用中间件了。 当然,中间件也是需要提前引入的。 

      上面代码中,redux-logger提供一个生成器createLogger,可以生成日志中间件logger。然后,将它放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。

      补充:我们怎么知道 redux 模块是否含有 applyMiddleware 和 createStore模块呢?  我们可以在 npm install redux --save 之后在node-modules中找到redux,然后在redux中找到入口文件,接着进一步找到内层文件,找到 export , 我们在es/utils/index.js中可以看到导出文件如下: 

      export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose };

      这样,引入applyMiddleware中间件就没有任何问题了。 

      需要注意的是:(1)createStore方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了。

    const store = createStore(
      reducer,
      initial_state,
      applyMiddleware(logger)
    );

      (2)中间件的次序有讲究。

    const store = createStore(
      reducer,
      applyMiddleware(thunk, promise, logger)
    );

    上面代码中,applyMiddleware方法的三个参数,就是三个中间件。有的中间件有次序要求,使用前要查一下文档。比如,logger就一定要放在最后,否则输出结果会不正确

     

    三、applyMiddlewares()

      上面我们使用了applyMiddlewares()方法,这个方法的作用就是使用中间件,之前,我们也已经找到其源码所在位置,现在粘贴如下所示:

      

    export default function applyMiddleware() {
      for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
        middlewares[_key] = arguments[_key];
      }
    
      return function (createStore) {
        return function (reducer, preloadedState, enhancer) {
          var store = createStore(reducer, preloadedState, enhancer);
          var _dispatch = store.dispatch;
          var chain = [];
    
          var middlewareAPI = {
            getState: store.getState,
            dispatch: function dispatch(action) {
              return _dispatch(action);
            }
          };
          chain = middlewares.map(function (middleware) {
            return middleware(middlewareAPI);
          });
          _dispatch = compose.apply(undefined, chain)(store.dispatch);
    
          return _extends({}, store, {
            dispatch: _dispatch
          });
        };
      };
    }

      即这里可以接受任意多的中间件,然后将所有中间件放在一个middlewares数组中。  接着返回一个函数,使用中间件,在阮一峰老师的博客中也有此段源代码,写法有不同之处,只是这里是es5他的是es6的语法。 

      

    四、异步操作的基本思路

      理解了中间件以后,就可以处理异步操作了。

           同步操作只要发出一种 Action 即可,异步操作的差别是它要发出三种 Action

    操作发起时的 Action
    操作成功时的 Action
    操作失败时的 Action

      以向服务器取出数据为例,三种 Action 可以有两种不同的写法。

    // 写法一:名称相同,参数不同
    { type: 'FETCH_POSTS' }
    { type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
    { type: 'FETCH_POSTS', status: 'success', response: { ... } }
    
    // 写法二:名称不同
    { type: 'FETCH_POSTS_REQUEST' }
    { type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
    { type: 'FETCH_POSTS_SUCCESS', response: { ... } }

      除了 Action 种类不同,异步操作的 State 也要进行改造,反映不同的操作状态。下面是 State 的一个例子。

    let state = {
      // ... 
      isFetching: true,
      didInvalidate: true,
      lastUpdated: 'xxxxxxx'
    };

      

    五、 redux-thunk 中间件

      这个中间件就可以解决异步actions的多次action触发问题。

    六、 redux-promise中间件

      既然 Action Creator 可以返回函数,当然也可以返回其他值。另一种异步操作的解决方案,就是让 Action Creator 返回一个 Promise 对象。

      即此中间件解决的问题和redux-thunk中间件解决的问题相似。 

  • 相关阅读:
    asp.net应用程序的生命周期和iis
    跨网页公布技术
    Java面试题:异常、静态变量
    js省市级联
    python基础之介绍
    Java学习(二)有关Tomcat的进一步理解与运用
    Java学习(一)环境的配置和软件的使用
    构造方法
    自言自语
    小小叹
  • 原文地址:https://www.cnblogs.com/zhuzhenwei918/p/7245407.html
Copyright © 2011-2022 走看看