浅谈Redux中间件的实践

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

为什么要使用中间件

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

中间件介绍

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

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

中间件的特点

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

中间件接口

每个中间件必须定义为一个函数,返回一个接受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项目中可以多加实践。

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

(0)

相关推荐

  • 简单介绍react redux的中间件的使用

    用过react的同学都知道在redux的存在,redux就是一种前端用来存储数据的仓库,并对改仓库进行增删改查操作的一种框架,它不仅仅适用于react,也使用于其他前端框架.研究过redux源码的人都觉得该源码很精妙,而本博文就针对redux中对中间件的处理进行介绍. 在讲redux中间件之前,先用两张图来大致介绍一下redux的基本原理: 图中就是redux的基本流程,这里就不细说. 一般在react中不仅仅利用redux,还利用到react-redux: react-redux这里也不细说.

  • redux中间件之redux-thunk的具体使用

    redux的核心概念其实很简单:将需要修改的state都存入到store里,发起一个action用来描述发生了什么,用reducers描述action如何改变state tree .创建store的时候需要传入reducer,真正能改变store中数据的是store.dispatch API. 1.概念 dispatch一个action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware.你可以利用 Redux middleware 来进行日志记录.创建崩溃报告.调用

  • 浅谈Redux中间件的实践

    最近项目前端开发框架采用React+Redux进行实现,但是,如何异步访问服务器端,以及想要在开发过程中进行状态树日志的输出,所以怎么才能解决这两个问题? 采用Redux中间件 为什么要使用中间件 在利用Redux进行状态管理时,用户在UI层面触发行为,一个action对象通过store.dispatch派发到Reducer进行触发,接下来Reducer会根据type来更新对应的Store上的状态树,更改后的state会触发对应组件的重新渲染.如下图所示.在这个流程中,action对象是一个同步

  • 浅谈laravel中间件的创建思路

    Laravel 中间件提供了一种机制在不修改逻辑代码的情况下,中断原本程序流程,通过中间件来处理一些事件,或者扩展一些功能.比如日志中间件可以方便的记录请求和响应日志,而不需要去更改逻辑代码. 那么我们简化一下软件执行过程,现在有一个核心类kernel,下面是它的laravel代码 #捕获请求 $request = Illuminate\Http\Request::capture() #处理请求 $response = $kernel->handle($request); 代码的作用是 捕获一个

  • 浅谈redux, koa, express 中间件实现对比解析

    如果你有 express ,koa, redux 的使用经验,就会发现他们都有 中间件(middlewares)的概念,中间件 是一种拦截器的思想,用于在某个特定的输入输出之间添加一些额外处理,同时不影响原有操作. 最开始接触 中间件是在服务端使用 express 和 koa 的时候,后来从服务端延伸到前端,看到其在redux的设计中也得到的极大的发挥.中间件的设计思想也为许多框架带来了灵活而强大的扩展性. 本文主要对比redux, koa, express 的中间件实现,为了更直观,我会抽取出

  • 浅谈redux以及react-redux简单实现

    写在前头 redux 简介 随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态). 这些 state 可能包括服务器响应.缓存数据.本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等. 管理不断变化的 state 非常困难.如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,

  • 浅谈express 中间件机制及实现原理

    简介 中间件机制可以让我们在一个给定的流程中添加一个处理步骤,从而对这个流程的输入或者输出产生影响,或者产生一些中作用.状态,或者拦截这个流程.中间件机制和tomcat的过滤器类似,这两者都属于责任链模式的具体实现. express 中间件使用案例 let express = require('express') let app = express() //解析request 的body app.use(bodyParser.json()) //解析 cookie app.use(cookieP

  • 浅谈Webpack自动化构建实践指南

    由于现在的博客是使用wordpress搭建,自己得经常修改过一些代码,但是修改第三方源码真的比较痛苦,于是决定计划开始使用React + Node.js / Python开发新博客项目,最终替换当前博客代码,方便以后博客的维护和更新,也能实现自我开发技术,架构设计,解决问题能力的提升,同时记录下整个开发历程,总结,分享,希望能与读者们一起进步.本篇介绍如何使用Webpack和Babel,Eslint,documentation.js等搭建项目开发环境和生产环境,也算项目的准备工作,下一期计划介绍

  • 浅谈Webpack 持久化缓存实践

    前言 最近在看 webpack 如何做持久化缓存的内容,发现其中还是有一些坑点的,正好有时间就将它们整理总结一下,读完本文你大致能够明白: 什么是持久化缓存,为什么做持久化缓存? webpack 如何做持久化缓存? webpack 做缓存的一些注意点. 持久化缓存 首先我们需要去解释一下,什么是持久化缓存,在现在前后端分离的应用大行其道的背景下,前端 html,css,js 往往是以一种静态资源文件的形式存在于服务器,通过接口来获取数据来展示动态内容.这就涉及到公司如何去部署前端代码的问题,所以

  • 浅谈Go Channel 高级实践

    channel 是 golang 里相当有趣的一个功能,在我使用 golang 编码的经验里,大部分事件都会是在享受 channel 和 goroutine 配合的乐趣.所以本文主要介绍 channel 的一些有趣的用法. 这里有 Oling Cat 翻译的Go编程语言规范里关于 channel(信道)的描述: 信道提供了一种机制,它在两个并发执行的函数之间进行同步,并通过传递(与该信道元素类型相符的)值来进行通信. 这个个描述又乏味.又枯燥.在我第一次阅读的时候,完全不明白这到底是个什么玩意.

  • 浅谈Go Slice 高级实践

    Go 语言切片是对数组的抽象. Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大. 定义切片 你可以声明一个未指定大小的数组来定义切片: var identifier []type 切片不需要说明长度. 或使用make()函数来创建切片: var slice1 []type = make([]type, len) 也可以简写为 slice1

  • 浅谈Laravel中的三种中间件的作用

    在之前一直简单的认为中间件就是往middleware里添加中间件即可.现在才知道中间件有三种类型,分别为:$middleware $middlewareGroup $routeMiddleware 通过查阅资料,终于明白了这三种类型的作用和不同. 第一种,全局中间件/$middleware: 我们的每一次请求,这里面的每个中间件都会执行. 第二种,路由中间件/$routeMiddleware: 定义在该属性内的中间件,只能在定义路由的时候引用. 假设这是我们定义的路由中间件: protected

随机推荐