浅谈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 相关文章推荐
Maps Javascript
Jan 22 Javascript
js 单引号 传递方法
Jun 22 Javascript
js相册效果代码(点击创建即可)
Apr 16 Javascript
js简易namespace管理器 实例代码
Jun 21 Javascript
iframe调用父页面函数示例详解
Jul 17 Javascript
js实现YouKu的漂亮搜索框效果
Aug 19 Javascript
基于JavaScript代码实现pc与手机之间的跳转
Dec 23 Javascript
详解微信小程序开发之城市选择器 城市切换
Jan 17 Javascript
JS出现失效的情况总结
Jan 20 Javascript
微信小程序开发之入门实例教程篇
Mar 07 Javascript
Node.js中你不可不精的Stream(流)
Jun 08 Javascript
js闭包的9个使用场景
Dec 29 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实现常用排序算法的方法
2020/02/05 PHP
JavaScript国旗变换效果代码
2008/08/13 Javascript
js监听表单value的修改同步问题,跨浏览器支持
2009/12/31 Javascript
基于jQuery的输入框在光标位置插入内容, 并选中
2011/10/29 Javascript
基于JQuery实现的类似购物商城的购物车
2011/12/06 Javascript
js怎么覆盖原有方法实现重写
2014/09/04 Javascript
javascript显示中文日期的方法
2015/06/18 Javascript
js实现的二级横向菜单条实例
2015/08/22 Javascript
JavaScript实现cookie的写入、读取、删除功能
2015/11/05 Javascript
javascript数据结构之二叉搜索树实现方法
2015/11/25 Javascript
js css+html实现简单的日历
2016/07/14 Javascript
AngularJS中update两次出现$promise属性无法识别的解决方法
2017/01/05 Javascript
原生js实现秒表计时器功能
2017/02/16 Javascript
浅谈react-router HashRouter和BrowserRouter的使用
2017/12/29 Javascript
动态内存分配导致影响Javascript性能的问题
2018/12/18 Javascript
python pycurl验证basic和digest认证的方法
2018/05/02 Python
Python键盘输入转换为列表的实例
2018/06/23 Python
Python装饰器模式定义与用法分析
2018/08/06 Python
Python语言检测模块langid和langdetect的使用实例
2019/02/19 Python
Python求均值,方差,标准差的实例
2019/06/29 Python
python使用自定义钉钉机器人的示例代码
2020/06/24 Python
使用matplotlib的pyplot模块绘图的实现示例
2020/07/12 Python
css3 flex布局 justify-content:space-between 最后一行左对齐
2020/01/02 HTML / CSS
Sephora丝芙兰菲律宾官方网站:购买化妆品和护肤品
2017/04/05 全球购物
Petmate品牌官方网站:宠物用品
2018/11/25 全球购物
乌克兰的第一家手表店:Deka
2020/03/05 全球购物
JSF面试题:Jsf中的核心类用那些?有什么作用?LiftCycle六大生命周期是什么?
2014/07/17 面试题
社会实践自我鉴定
2013/11/07 职场文书
办理居住证介绍信
2014/01/15 职场文书
总经理司机职责
2014/02/02 职场文书
人事专员岗位职责范本
2014/03/04 职场文书
教室标语大全
2014/06/21 职场文书
镇党政领导班子民主生活会思想汇报
2014/10/11 职场文书
2015年班干部工作总结
2015/04/29 职场文书
如何设计高效合理的MySQL查询语句
2021/05/26 MySQL
Python OpenCV超详细讲解基本功能
2022/04/02 Python