浅谈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 相关文章推荐
window.location和document.location的区别分析
Dec 23 Javascript
jQuery AJAX 调用WebService实现代码
Mar 24 Javascript
高性能WEB开发 flush让页面分块,逐步呈现 flush让页面分块,逐步呈现
Jun 19 Javascript
JQuery获取当前屏幕的高度宽度的实现代码
Jul 12 Javascript
十大 Node.js 的 Web 框架(快速提升工作效率)
Jun 30 Javascript
Vue 中批量下载文件并打包的示例代码
Nov 20 Javascript
详解webpack中的hash、chunkhash、contenthash区别
Jan 05 Javascript
vue.js中npm安装教程图解
Apr 10 Javascript
详解react内联样式使用webpack将px转rem
Sep 13 Javascript
微信小程序实现跳转的几种方式总结(推荐)
Apr 24 Javascript
React+TypeScript+webpack4多入口配置详解
Aug 08 Javascript
vue 通过 Prop 向子组件传递数据的实现方法
Oct 30 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
ThinkPHP单字母函数(快捷方法)使用总结
2014/07/23 PHP
分享十款最出色的PHP安全开发库中文详细介绍
2015/03/22 PHP
laravel5.2实现区分前后台用户登录的方法
2017/01/11 PHP
当鼠标滑过文本框自动选中输入框内容的JS代码分享
2013/11/26 Javascript
javascript实现全角与半角字符的转换
2015/01/07 Javascript
jQuery实现DIV层淡入淡出拖动特效的方法
2015/02/13 Javascript
jQuery+ajax+asp.net获取Json值的方法
2016/06/08 Javascript
第二篇Bootstrap起步
2016/06/21 Javascript
快速移动鼠标触发问题及解决方法(ECharts外部调用保存为图片操作及工作流接线mouseenter和mouseleave)
2016/08/29 Javascript
AngularJS学习笔记(三)数据双向绑定的简单实例
2016/11/08 Javascript
js实现页面刷新滚动条位置不变
2016/11/27 Javascript
详解vue嵌套路由-query传递参数
2017/05/23 Javascript
vue.js实现点击后动态添加class及删除同级class的实现代码
2018/04/04 Javascript
nuxt 每个页面head标签内容设置方式
2020/11/05 Javascript
[03:26]《DAC最前线》之EG经理自述DOTA2经历
2015/02/02 DOTA
[57:50]DOTA2上海特级锦标赛主赛事日 - 4 胜者组决赛Secret VS Liquid第二局
2016/03/05 DOTA
Python处理RSS、ATOM模块FEEDPARSER介绍
2015/02/18 Python
python3.x实现发送邮件功能
2018/05/22 Python
启动Atom并运行python文件的步骤
2018/11/09 Python
对PyQt5的输入对话框使用(QInputDialog)详解
2019/06/25 Python
利用python开发app实战的方法
2019/07/09 Python
Django框架 querySet功能解析
2019/09/04 Python
python中web框架的自定义创建
2019/09/08 Python
Python基础之函数基本用法与进阶详解
2020/01/02 Python
纯CSS3打造属于自己的“小黄人”
2016/03/14 HTML / CSS
美国存储和组织商店:The Container Store
2017/08/16 全球购物
亚洲领先的旅游体验市场:Voyagin
2019/11/23 全球购物
奥地利票务门户网站:oeticket.com
2019/12/31 全球购物
毕业生个人求职的自我评价
2013/10/28 职场文书
纪律委员竞选稿
2015/11/19 职场文书
django如何自定义manage.py管理命令
2021/04/27 Python
浅谈python中的多态
2021/06/15 Python
MySQL8.0的WITH查询详情
2021/08/30 MySQL
Python机器学习应用之工业蒸汽数据分析篇详解
2022/01/18 Python
使用python创建股票的时间序列可视化分析
2022/03/03 Python
windows10 家庭版下FTP服务器搭建教程
2022/08/05 Servers