浅谈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 相关文章推荐
超清晰的document对象详解
Feb 27 Javascript
jQuery中的.bind()、.live()和.delegate()之间区别分析
Jun 08 Javascript
页面调用单个swf文件,嵌套出多个方法。
Nov 21 Javascript
script不刷新页面的联动前后代码
Sep 18 Javascript
javascript弹出层输入框(示例代码)
Dec 11 Javascript
JavaScript实现添加、查找、删除元素
Jul 02 Javascript
jQuery对象的链式操作用法分析
May 10 Javascript
JS实现页面跳转参数不丢失的方法
Nov 28 Javascript
JS实现的系统调色板完整实例
Dec 21 Javascript
vue单页缓存方案分析及实现
Sep 25 Javascript
详解基于node.js的脚手架工具开发经历
Jan 28 Javascript
vue 数据双向绑定的实现方法
Mar 04 Vue.js
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用正则表达式匹配URL的简单方法
2013/11/12 PHP
PHP实现扎金花游戏之大小比赛的方法
2015/03/10 PHP
PHP new static 和 new self详解
2017/02/19 PHP
javascript在一段文字中的光标处插入其他文字
2007/08/26 Javascript
利用WebBrowser彻底解决Web打印问题(包括后台打印)
2009/06/22 Javascript
jquery.boxy弹出框(后隔N秒后自动隐藏/自动跳转)
2013/01/15 Javascript
javascrip关于继承的小例子
2013/05/10 Javascript
jquery拖动插件(jquery.drag)使用介绍
2013/06/18 Javascript
jquery实现当滑动到一定位置时固定效果
2014/06/17 Javascript
Mac OS X 系统下安装和部署Egret引擎开发环境
2014/09/03 Javascript
jQuery中attr()方法用法实例
2015/01/05 Javascript
jQuery实现文本框输入同步的方法
2015/06/20 Javascript
jQuery实现宽屏图片轮播实例教程
2015/11/24 Javascript
JS 动态判断PC和手机浏览器实现代码
2016/09/21 Javascript
深入探究node之Transform
2017/07/20 Javascript
浅谈jquery中ajax跨域提交的时候会有2次请求的问题
2017/11/10 jQuery
微信小程序使用wxParse解析html的方法教程
2018/07/06 Javascript
electron实现qq快捷登录的方法示例
2018/10/22 Javascript
浅谈KOA2 Restful方式路由初探
2019/03/14 Javascript
vue 根据选择条件显示指定参数的例子
2019/11/09 Javascript
解决vue prop传值default属性如何使用,为何不生效的问题
2020/09/21 Javascript
python轻松实现代码编码格式转换
2015/03/26 Python
Python正则匹配判断手机号是否合法的方法
2020/12/09 Python
Python/Django后端使用PIL Image生成头像缩略图
2019/04/30 Python
用Python实现BP神经网络(附代码)
2019/07/10 Python
django有外键关系的两张表如何相互查找
2020/02/10 Python
python爬虫beautifulsoup解析html方法
2020/12/07 Python
JAVA的事件委托机制和垃圾回收机制
2014/09/07 面试题
环境科学专业个人求职信
2013/09/26 职场文书
学前教育毕业生自荐信
2013/10/29 职场文书
师范生个人推荐信
2013/11/29 职场文书
怎样写好自我鉴定
2013/12/04 职场文书
奥巴马经典演讲稿
2014/09/13 职场文书
群众路线四风问题整改措施
2014/09/27 职场文书
2015年妇女工作总结
2015/05/14 职场文书
如何将numpy二维数组中的np.nan值替换为指定的值
2021/05/14 Python