Redux中的middleware其实就像是给你提供一个在action发出到实际reducer执行之前处理一些事情的机会。可以允许我们添加自己的逻辑在这段当中。它提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。

加入middleware后,整个数据的流动如下图所示:

举个简单的例子,我们使用middleware将每次action的执行详细信息都打出来。就用官方demo中的todoApp来举例,我们先实现一个简单的reducer用来添加一个todo:

  1. const todoApp = (state = {todo: []}, action) => {
  2. if (action.type === ‘addTodo’) {
  3. state.todo = […state.todo, action.value]
  4. }
  5. return state
  6. }

然后再补上其他逻辑测试,用最原始的方法实现将每次action的执行信息log出来:

  1. const redux = require(‘redux’)
  2. const todoApp = (state = {todo: []}, action) => {
  3. if (action.type === ‘addTodo’) {
  4. state.todo = […state.todo, action.value]
  5. }
  6. return state
  7. }
  8. let store = redux.createStore(todoApp);
  9. const action = {
  10. type: ‘addTodo’,
  11. value: ‘todo’,
  12. }
  13. console.log(‘state: ‘, store.getState());
  14. console.log(‘action: ‘, action);
  15. store.dispatch(action)
  16. console.log(‘next state: ‘, store.getState())

如果不出什么意外的话,我们这段代码应该会成功运行,并且将这段log出这个action的运行情况, 如下图:

接下来我们将log这件事尝试使用redux的middleware来完成。

首先,根据我们之前的了解,middleware其实是一段在action到reducer之间的处理逻辑。我们都知道,标准的一个redux发送一个action是调用store自身的dispatch方法。那么,我们想要在一个action到达reducer之前去做些处理的话,最好的地方应该就是尝试将store的dispatch替换为我们自己的,在其中加上我们的处理逻辑,例如打印log这件事。

  1. let store = redux.createStore(todoApp);
  2. const next = store.dispatch;
  3. const dispatchWithLog = (action) => {
  4. console.log(‘state: ‘, store.getState());
  5. console.log(‘action: ‘, action);
  6. next(action);
  7. console.log(‘next state: ‘, store.getState());
  8. }
  9. store.dispatch = dispatchWithLog;

我们将默认store的dispatch替换为自己的dispatchWithLog, 通过这种方式,完成了我们的需求,只要任何地方调用了store的diapatch去发送新的action, 我们都能讲其log出来,这个看起来已经有一点middleware的意思了。

虽然上面已经可以解决问题了,并且已经有点middleware的意思了,但是还有一点硬伤就是,需求多了就比较难搞了,例如就像官方上既需要log又需要Crash Reporting, 再通过这种方式去处理就显得很不优雅。Crash Reporting的middle的一个简单实现如下:

  1. const next1 = store.dispatch;
  2. const dispatchWithCreshReporting = (action) => {
  3. try {
  4. next1(action);
  5. } catch (err) {
  6. console.error(err);
  7. }
  8. }
  9. store.dispatch = dispatchWithCreshReporting;

当然,redux本身给我们提供了包装过后的工具方法来专门应用middleware。其中也不是简单粗暴的替换store上的dispatch了。这个方法即为applyMiddleware。

官方的doc也给出了一个关于applyMiddleware的一个简单粗暴的直接替换dispatch的一个示例,如下:

  1. function applyMiddlewareByMonkeypatching(store, middlewares) {
  2. middlewares = middlewares.slice()
  3. middlewares.reverse()
  4. // Transform dispatch function with each middleware.
  5. middlewares.forEach(middleware =>
  6. store.dispatch = middleware(store)
  7. )
  8. }

关于先逆序middlewares再进行替换,这里主要是为了,让middleware的执行顺序按照我么传给他的array顺序来进行。就像我们上面直接替换的那个例子,越往后面进行替换dispatch的在执行过程中先运行。

当然,官放的具体实现中不是这么简单粗暴的直接替换的方式,因为一来不够优雅,这种方式在链式的调用过程中有可能出现问题。比如某一个middleware并不是同步执行的,这样在进行store.dispatch = middleware(store)就有可能到下一个middleware时,store.dispatch还没有被替换。因此,官方的middleware是接受一个next的参数来,来拿到dispatch,并不是直接从store上对dispatch进行操作的。

一般一个标准的middleware是这个样子的,我们使用最初的log的那个middleware来举例,让它接受一个next(就是一个下一个的dispatch方法),再返回一个dispatch方法。

  1. function logger(store) {
  2. return function(next) {
  3. return function(action) {
  4. console.log(‘state: ‘, store.getState());
  5. console.log(‘action: ‘, action);
  6. let result = next(action);
  7. console.log(‘next state: ‘, store.getState());
  8. return result;
  9. }
  10. }
  11. }
  12. function creshReporting(store) {
  13. return function(next) {
  14. return function(action) {
  15. try {
  16. return next(action);
  17. } catch (err) {
  18. console.error(err);
  19. return next;
  20. }
  21. }
  22. }
  23. }

然后假设我们在apply时这样应用一下:

  1. function applyMiddleware(store, middlewares = [logger, crashReporting]) {
  2. middlewares = middlewares.slice()
  3. middlewares.reverse()
  4. let dispatch = store.dispatch
  5. middlewares.forEach(middleware =>
  6. dispatch = middleware(store)(dispatch)
  7. )
  8. return Object.assign({}, store, { dispatch })
  9. }

这样就能够进行优雅的链式调用了。并且用上ES6箭头函数后,这样写出来会更加的优雅:

  1. const logger = store => next => action => {
  2. console.log(‘state: ‘, store.getState());
  3. console.log(‘action: ‘, action);
  4. let result = next(action);
  5. console.log(‘next state: ‘, store.getState());
  6. return result;
  7. }

最后,其实redux middleware使用起来其实是非常的方便的,只需要记住applyMiddleware这个API即可。即const store = createStore(reducer, applyMiddleware(middlewares))

分享完毕,喜欢搭建可以喜欢蜘蛛表格小编的分享,以后还会给大家分享更多内容

如何学习理解Redux Middleware的更多相关文章

  1. redux middleware 的理解

    前言 这几天看了redux middleware的运用与实现原理,写了一个百度搜索的demo,实现了类似redux-thunk和redux-logger中间件的功能. 项目地址:https://git ...

  2. 理解Redux以及如何在项目中的使用

    今天我们来聊聊Redux,这篇文章是一个进阶的文章,建议大家先对redux的基础有一定的了解,在这里给大家推荐一下阮一峰老师的文章: http://www.ruanyifeng.com/blog/20 ...

  3. 3.3 理解 Redux 中间件(转)

    这一小节会讲解 redux 中间件的原理,为下一节讲解 redux 异步 action 做铺垫,主要内容为: Redux 中间件是什么 使用 Redux 中间件 logger 中间件结构分析 appl ...

  4. 理解 Redux 中间件机制

    Redux 的 action 是一个 JS 对象,它表明了如何对 store 进行修改.但是 Redux 的中间件机制使action creator 不光可以返回 action 对象,也可以返回 ac ...

  5. 全面学习理解TLB(Translation Look-aside Buffer)地址变换高速缓存

    全面学习理解TLB(Translation Look-aside Buffer)地址变换高速缓存 前言: 本文学习思路是:存在缘由   --> 存在好处 --> 定义性质 --> 具 ...

  6. 轻松理解Redux原理及工作流程

    轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...

  7. MLT的学习理解

    MLT的学习理解 MLT是一个开源的多媒体库,我们的音视频编辑工具,是使用它作为底层支持,某司的'快剪辑'pc版和安卓版,也是用的它. MLT简介 它的GitHub地址,这个库比较老了,现在只有一个作 ...

  8. 菜鸟之路——机器学习之SVM分类器学习理解以及Python实现

    SVM分类器里面的东西好多呀,碾压前两个.怪不得称之为深度学习出现之前表现最好的算法. 今天学到的也应该只是冰山一角,懂了SVM的一些原理.还得继续深入学习理解呢. 一些关键词: 超平面(hyper ...

  9. batch normalization学习理解笔记

    batch normalization学习理解笔记 最近在Andrew Ng课程中学到了Batch Normalization相关内容,通过查阅资料和原始paper,基本上弄懂了一些算法的细节部分,现 ...

随机推荐

  1. Webstorm 配置 Less编译

    配置less编译

  2. linux ioctl 接口

    大部分驱动需要 -- 除了读写设备的能力 -- 通过设备驱动进行各种硬件控制的能力. 大 部分设备可进行超出简单的数据传输之外的操作; 用户空间必须常常能够请求, 例如, 设 备锁上它的门, 弹出它的 ...

  3. linux获知当前时间

    内核代码能一直获取一个当前时间的表示, 通过查看 jifies 的值. 常常地, 这个值只代 表从最后一次启动以来的时间, 这个事实对驱动来说无关, 因为它的生命周期受限于系统 的 uptime. 如 ...

  4. js中的克隆

    1.如果克隆对象是基本类型,直接复制就可以 <script type="text/javascript"> var str1 = 'abc' var str2 = st ...

  5. ASP.NET MVC4.0+EF+LINQ+bui+bootstrap+网站+角色权限管理系统(3)

    接下来完成用户.角色的增删查改,以及用户角色.权限的设置 对用户表.角色表做了一些扩展如下[可以更加自己需要增减字段] 相应的M_UserProfile.cs.M_Roles.cs进行扩展 using ...

  6. java打包上传服务器的一些命令

    Maven下package打包成jar包和war包,都在target目录下 其中War包扔在tomcat的webapps目录下.随tomcat启动自行启动 运行jar包命令. nohup java - ...

  7. CodeForces Goodbye 2017

    传送门 A - New Year and Counting Cards •题意 有n张牌,正面有字母,反面有数字 其中元音字母$a,e,o,i,u$的另一面必须对应$0,2,4,6,8$的偶数 其他字 ...

  8. ie6,7不支持display:inline-block;

    解决方案: display:inline-block; *display:inline;*zoom:1; ie6:* _ ,ie7:*

  9. ELK系统分析nginx日志

    一.nginx nginx 服务器日志的log_format格式: log_format main '$remote_addr - $remote_user [$time_local] "$ ...

  10. 33.python之操作系统,进程,线程

    转载:https://www.cnblogs.com/yuanchenqi/articles/6248025.html 操作系统 一 为什么要有操作系统? 现代计算机系统是由一个或者多个处理器,主存, ...