浅谈Redux中间件的实践


Posted in Javascript onJuly 27, 2018

最近项目前端开发框架采用React+Redux进行实现,但是,如何异步访问服务器端,以及想要在开发过程中进行状态树日志的输出,所以怎么才能解决这两个问题? 采用Redux中间件

为什么要使用中间件

在利用Redux进行状态管理时,用户在UI层面触发行为,一个action对象通过store.dispatch派发到Reducer进行触发,接下来Reducer会根据type来更新对应的Store上的状态树,更改后的state会触发对应组件的重新渲染。如下图所示。在这个流程中,action对象是一个同步的对象,是一个包含type字段的简单对象,但是在访问服务器时,由于浏览器是单线程的,不会一遍渲染组件一遍等待服务器返回的结果,因此我们需要设计一种异步访问服务器的方法来实现与服务器端的正常通信。

浅谈Redux中间件的实践

中间件介绍

在Redux架构下,一个action对象在通过store.dispatch派发,在调用reducer函数前,会先经过一个中间件环节,如下图所示。

浅谈Redux中间件的实践

从上图可以看出,在一个Redux架构中可以用多个中间件,这些中间件一起组织处理请求的“管道”。一个中间件是一个独立的函数,可以组合使用,中间件有一个统一的接口,正因为一个中间件只能完成一个特定的功能,所以把多个中间件组合在一起才能满足比较丰富的应用需求。当然在使用时,也需要按照顺序依次处理传入的action,只有排在前面的中间件完成任务之后,后面的中间件才能有机会继续处理action。

浅谈Redux中间件的实践

中间件的特点

  • 中间件是独立的函数
  • 中间件可以组合使用
  • 中间件有一个统一的接口

中间件接口

每个中间件必须定义为一个函数,返回一个接受next参数的函数,而这个接受next参数的函数又返回一个接受action参数的函数。next参数本身也是一个函数,中间件调用这个next函数通知Redux自己的处理工作已经结束。

一个什么都不做的中间件代码如下:

function doNothingMiddleware({dispatch, getState}) {
  return function(next){
   return function(action){
    return next(action);
    }
  }
}

包含的功能有:

  • 调用dispatch派发出一个新的action对象
  • 调用getState获得当前Redux Store上的状态
  • 调用next告诉Redux当前中间件工作完毕,让Redux调用下一个中间件
  • 访问action对象action上的所有数据

在一个Redux应用如果想要使用中间件,必须通过applyMiddleware来生成。Redux的源码文件非常简单,由五个文件一起组成,分别是createStore.js,applyMiddlware.js,compose.js,bindActionCreator.js,combineReducers.js。与createStore是用来创建一个状态树,并且暴露出几个方法,包括dispatch,subscribe,getState,replaceReducer和$$observable,给createStore传入的参数有reducer,preloadedState和enhancer,其中enhancer就是一个store增强器,是一个函数,只能用applyMiddleware生成。applyMiddleware函数是根据外部函数(中间件函数)包装原来的dispatch函数,然后将新的dispatch函数暴露出去。

//根据外部函数(中间件函数)包装原来的dispatch函数,然后将新的dispatch函数暴露了出去
export default function applyMiddleware(...middlewares) {
 //return一个函数,它可以接受createStore方法作为参数,给返回的store的dispatch方法再进行一次包装
 return createStore => (...args) => {//agrs包含reducer, preloadedState, enhancer
  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方法
  dispatch = compose(...chain)(store.dispatch)

  return {
   ...store,
   dispatch
  }
 }
}

中间件与增强器的区别

中间件和增强器都是对Redux Store的增强,但是中间件仅仅是对Redux Store的dispatch方法进行了增强,也就是从dispatch函数调用到action对象被reducer处理这个过程中的操作,增强器是对Redux Store进行更深层次的增强定制,需要使用Store Enhancer,通过阅读增强器接口,一个增强器其实利用随给的参数创造出一个store对象,然后定制对象,最后把Store对象返回。总的对比如下:

  • 中间件: 可以用来增强redux store的dispatch函数,也就是从dispatch函数调用到action对象被reducer处理这个过程中的操作
  • 增强器: 对redux store进行更深层次的增强定制,可以增强redux store的各个方面。

异步访问服务器:

异步action对象

在没有引入中间件时,社会治理子系统在开发时,所有的action都是同步的,一个同步的action对象是一个包含type字段的简单对象,但是我们需要实现一个异步action对象,是一个函数,在action触发之后,在reducer接收到执行命令之前可以进行一个异步操作。

我们引入redux-thunk来实现异步访问服务器方法,一个访问服务器的action,至少要涉及三个action类型:

  • 表示异步操作已经开始的action类型;
  • 表示异步操作成功的action类型;
  • 表示异步操作失败的action类型;

Redux-thunk源代码解析

Redux-thunk中间件是Redux中异步操作的解决方法之一,在action对象被reducer函数处理之前,是插入异步功能的时机,代码非常简单:

function create ThunkMiddleware(extraArgument){
  return ({dispatch, getState}) => next => action => {
    if(typeof action === ‘function'){
     return action(dispatch, getState, extraArgument);
   }
   return next(action)
  }
}
const thunk = createThunkMiddleware();
export default thunk;

createThunkMiddleware函数返回了一个函数,是实际处理每个action对象的函数,首先检查参数action的类型,如果是函数类型的话,就执行这个action函数,把dispatch和getState

作为参数传递出去,否则就调用next让下一个中间件继续处理action。

Redux-thunk的使用:

首先,安装redux-thunk,在已经安装了node.js的命令窗口中运行 “npm install redux-thunk --save-dev”,在store.js中引入redux-thunk,并且确保redux的applyMiddleware函数也引入。具体实现代码如下。

import {createStore, combineReducers, applyMiddleware} from ‘redux';
import {otherState, dataState} from ‘reducers';
import thunkMiddleware from ‘redux-thunk';
var reducers = combineReducers({
  otherState,
  dataState
});
var store = createStore(reducers, applyMiddleware(thunkMiddleware));
export default store;

在成功引入了redux-thunk后,我们也要设计异步操作的action对象,例如,在设备管理模块中,成功保存设备信息后要重新获取设备信息,代码如下:

function saveInfo(params){
  let url = “/api/device”;
  return function(dispatch, getState){
    dispatch(saveInfoRequest());
    return Http.get(url, {
      params: params
    }).then(res=>{
      if(res && res.type === 0){
        dispatch(saveInfoSuccess ());
        let dataState = getState().dataState;
        let newParams = {
          start: dataState.start,
          limit: dataState.limit,
          searchName: dataState.searchName
        };
        dispatch(getInfo(newParams))
      }
    }).catch(error=>{
       dispatch(saveInfoFailure (error));
    });
  }
}

从这个saveDeviceInfo返回的函数中,不仅可以dispatch一个同步的action对象,还可派发另一个异步action对象,来满足一些有着先后关系的业务逻辑,代码可读性要比用Promise实现起来代码更加清晰。

Redux-logger使用

在开发阶段,我们需要对redux数据流中每个流程进行监控,需要log输出,redux-logger是官方推荐的一款日志中间件,使用起来非常方便。当然,要使redux-logger生效,需要保证在系统中使用redux进行状态管理,否则没有任何日志输出。
Redux-logger的使用方法可以分为两种,基本使用方法如下:

import { applyMiddleware, createStore} from ‘redux';
import logger from ‘reudx-logger'
const store = createStore(
  Reducer,
  applyMiddleware(logger)
)

也可以自己写一个日志输出中间件,代码如下:

var logger = store => next => action => {
  console.log('[action]', action)
  console.log(`[action] type:${action.type} payload:${JSON.stringify(action.payload)}`)
  next(action)
  console.log('[store]', store.getState())
  console.log(`[store] ${JSON.stringify(store.getState())}`)
}

总结

Redux中间件可以增强Store.dispatch方法,多个中间件可以组成“管道”,按照顺序去处理action对象,在依次处理过后,才会有机会被reducer处理。中间件的应用场景很多,除了可以支持异步访问服务器,还有许多很好的中间件插件,例如react-addons-perf进行调试,和redux-logger来记录状态,也可以根据业务需求来自己编写中间件,应用非常灵活,在其他react项目中可以多加实践。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
简单几行JS Code实现IE邮件转发新浪微博
Jul 03 Javascript
javascript基本类型详解
Nov 28 Javascript
JavaScript实现查找字符串中第一个不重复的字符
Dec 29 Javascript
探讨JavaScript语句的执行过程
Jan 28 Javascript
简单实现js间歇或无缝滚动效果
Jun 29 Javascript
超全面的javascript中变量命名规则
Feb 09 Javascript
JavaScript实现获取远程的html到当前页面中
Mar 26 Javascript
node.js-v6新版安装具体步骤(分享)
Sep 06 Javascript
js动态引入的四种方法
May 05 Javascript
Vue项目中跨域问题解决方案
Jun 05 Javascript
JavaScript 俄罗斯方块游戏实现方法与代码解释
Apr 08 Javascript
js实现前端界面导航栏下拉列表
Aug 27 Javascript
JavaScript多态与封装实例分析
Jul 27 #Javascript
Vue配合iView实现省市二级联动的示例代码
Jul 27 #Javascript
react native 文字轮播的实现示例
Jul 27 #Javascript
Vue render渲染时间戳转时间,时间转时间戳及渲染进度条效果
Jul 27 #Javascript
浅析Vue项目中使用keep-Alive步骤
Jul 27 #Javascript
在vue中使用Autoprefixed的方法
Jul 27 #Javascript
JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例
Jul 27 #Javascript
You might like
谈谈PHP语法(5)
2006/10/09 PHP
php文件夹与文件目录操作函数介绍
2013/09/09 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(三)
2014/06/23 PHP
Yii中使用PHPExcel导出Excel的方法
2014/12/26 PHP
php编程每天必学之表单验证
2016/03/01 PHP
PHP实现获取ip地址的5种方法,以及插入用户登录日志操作示例
2019/02/28 PHP
用js 让图片在 div或dl里 居中,底部对齐
2008/01/21 Javascript
最简单的jQuery程序 入门者学习
2009/07/09 Javascript
html5的自定义data-*属性和jquery的data()方法的使用示例
2013/08/21 Javascript
javascript实现给定半径求出圆的面积
2015/06/26 Javascript
Bootstrap每天必学之导航条
2015/11/27 Javascript
基于Bootstrap实现下拉菜单项和表单导航条(两个菜单项,一个下拉菜单和登录表单导航条)
2016/07/22 Javascript
使用jquery给指定的table动态添加一行、删除一行
2016/10/13 Javascript
js设置和获取自定义属性的方法
2016/10/20 Javascript
MUI 解决动态列表页图片懒加载再次加载不成功的bug问题
2017/04/13 Javascript
javascript简单写的判断电话号码实例
2017/05/24 Javascript
微信小程序列表中item左滑删除功能
2018/11/07 Javascript
vue组件文档(.md)中如何自动导入示例(.vue)详解
2019/01/25 Javascript
Vue使用axios出现options请求方法
2019/05/30 Javascript
解决layui使用layui-icon出现默认图标的问题
2019/09/11 Javascript
Node.js实现简单管理系统
2019/09/23 Javascript
[01:32]DOTA2次级联赛——首支职业女子战队选拔赛全记录
2014/10/23 DOTA
[01:33:30]DOTA2-DPC中国联赛 正赛 RNG vs Phoenix BO3 第二场 2月5日
2021/03/11 DOTA
[55:26]DOTA2-DPC中国联赛 正赛 Aster vs LBZS BO3 第一场 2月23日
2021/03/11 DOTA
Python的Bottle框架的一些使用技巧介绍
2015/04/08 Python
浅谈Python的异常处理
2016/06/19 Python
python运用sklearn实现KNN分类算法
2019/10/16 Python
python开发前景如何
2020/06/11 Python
为什么说python适合写爬虫
2020/06/11 Python
适合各种场合的美食礼品:Harry & David
2016/08/03 全球购物
毕业生个人的求职信范文
2013/12/03 职场文书
小学生成长感言
2014/01/30 职场文书
2014离婚协议书范文
2014/09/10 职场文书
2015年校医个人工作总结
2015/07/24 职场文书
python使用tkinter实现透明窗体上绘制随机出现的小球(实例代码)
2021/05/17 Python
python编程学习使用管道Pipe编写优化代码
2021/11/20 Python