1. 中间件概述

目标:能够理解为什么需要 redux 中间件

知识点:

  1. 默认情况下,Redux 自身只能处理同步数据流。但是在实际项目开发中,状态的更新、获取,通常是使用异步操作来实现。
  • 问题:如何在 Redux 中进行异步操作呢 ?
  • 回答:通过 Redux 中间件机制来实现 !
  1. Redux 中间件作用:处理具有副作用(side effect)的功能,比如,异步操作就是最常见的 side effect

  2. 中间件说明:

  • 中间件,可以理解为处理一个功能的中间环节
  • 下图中,自来水从水库到用户家庭中的每一个环节都是一个中间件
  • 中间件的优势:可以串联、组合,在一个项目中使用多个中间件
  • Redux 中间件用来处理 状态 更新,也就是在 状态 更新的过程中,执行一系列的相应操作

image.png

总结:

默认情况下,Redux 自身只能处理同步数据流
在 Redux 中进行异步操作,需要通过 Redux 中间件机制来实现

2. 中间件的触发时机

目标:能够理解中间件的触发时机

知识点:

Redux 中间件执行时机:在 dispatching action 和 到达 reducer 之间

  1. 没有中间件:dispatch(action) => reducer

image.png

  1. 使用中间件:dispatch(action) => 执行中间件代码 => reducer

image.png

  1. 原理:封装了 redux 的 dispatch 方法
  • 没有中间件:store.dispatch() 就是 Redux 库自己提供的 dispatch 方法,用来发起状态更新
  • 使用中间件:store.dispatch() 就是 中间件 封装处理后的 dispatch 方法,但是,最终一定会调用 Redux 自己的 dispatch 方法发起状态更新

总结:

  • Redux 中间件执行时机:在 dispatching action 和 到达 reducer 之间
  • 没有中间件:store.dispatch() 就是 Redux 库自己提供的 dispatch 方法
  • 使用中间件:store.dispatch() 就是 中间件 封装处理后的 dispatch 方法

3. redux-logger 中间件

目标:能够使用redux-logger中间件记录日志

使用步骤:

  1. 安装:yarn add redux-logger
  2. 导入 redux-logger 中间件
  3. 从 redux 中导入 applyMiddleware 函数
  4. 调用 applyMiddleware() 并传入 logger 中间件作为参数
  5. 将 applyMiddleware() 调用作为 createStore 函数的第二个参数

然后,调用 store.dispatch() 查看 console 中 logger 中间件记录的日志信息

落地代码:

  1. // 导入 applyMiddleware 函数
  2. import { createStore, applyMiddleware } from 'redux'
  3. // 导入 logger 中间件
  4. import logger from 'redux-logger'
  5. import rootReducer from './reducers'
  6. // 将 applyMiddleware 方法传入第二个参数
  7. // 将 logger 中间件作为参数传入 applyMiddleware 方法
  8. const store = createStore(rootReducer, applyMiddleware(logger))

4. Redux 中间件原理

目标:了解中间件的实现原理

知识点:

Redux 中间件原理:创建一个函数,包装 store.dispatch,使用新创建的函数作为新的 dispatch

  1. 比如下图,logger 就是一个中间件,使用该中间件后 store.dispatch 就是包装后的新 dispatch
  2. 中间件修改了 store.dispatch,在分发动作和到达 reducer 之间提供了扩展

image.png

  1. redux 中间件采用了 洋葱模型 来实现

洋葱内的每一层都表示一个独立的中间件,用于实现不同的功能,比如异常处理、打印日志等。每次请求都会从左侧开始一层层地经过每层的中间件,当进入到最里层的中间件后,就会从最里层的中间件开始逐层返回。因此对于每层的中间件来说,都在一个 请求 和 响应 周期中,都有两个时机点来添加不同的处理逻辑。
image.png
image.png

5. Redux 中间件原理代码演示

目标:通过代码的方式加深对 redux 中间件的理解

落地代码:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <script src="https://cdn.bootcdn.net/ajax/libs/redux/5.0.0-alpha.0/redux.min.js"></script>
  5. </head>
  6. <body>
  7. <script>
  8. function middleware1(store) {
  9. return function (next) {
  10. return function (action) {
  11. console.log('A middleware1 开始')
  12. next(action)
  13. console.log('B middleware1 结束')
  14. }
  15. }
  16. }
  17. function middleware2(store) {
  18. return function (next) {
  19. return function (action) {
  20. console.log('C middleware2 开始')
  21. next(action)
  22. console.log('D middleware2 结束')
  23. }
  24. }
  25. }
  26. function middleware3(store) {
  27. return function (next) {
  28. return function (action) {
  29. console.log('E middleware3 开始')
  30. next(action)
  31. console.log('F middleware3 结束')
  32. }
  33. }
  34. }
  35. function reducer(state, action) {
  36. if (action.type === 'middleware') {
  37. console.log('-----------------')
  38. console.log('- 华丽丽的分隔线-')
  39. console.log('-----------------')
  40. }
  41. return {}
  42. }
  43. var store = Redux.createStore(
  44. reducer,
  45. Redux.applyMiddleware(
  46. middleware1,
  47. middleware2,
  48. middleware3
  49. )
  50. )
  51. store.dispatch({ type: 'middleware' })
  52. </script>
  53. </body>
  54. </html>

控制台输出

  1. middleware1 开始
  2. middleware2 开始
  3. middleware3 开始
  4. -----------------
  5. - 华丽丽的分隔线-
  6. -----------------
  7. middleware3 结束
  8. middleware2 结束
  9. middleware1 结束

触发流程图:

  1. --------------------------------------
  2. | middleware1 |
  3. | ---------------------------- |
  4. | | middleware2 | |
  5. | | ------------------- | |
  6. | | | middleware3 | | |
  7. | | | | | |
  8. next next next ——————————— | | |
  9. dispatch —————————————> | reducer | 收尾工作->|
  10. nextState <————————————— | G | | | |
  11. | A | C | E ——————————— F | D | B |
  12. | | | | | |
  13. | | ------------------- | |
  14. | ---------------------------- |
  15. --------------------------------------
  16. 顺序 A -> C -> E -> G -> F -> D -> B
  17. \---------------/ \----------/
  18. 更新 state 完毕 收尾工作

6. 手写 Redux-logger 中间件

目标:加深如 Redux 中间件原理的理解

案例分析:

中间件的固定格式

  1. // store 表示:redux 的 store
  2. // next 表示:下一个中间件,
  3. // 如果只使用一个中间,那么 next 就是 store.dispatch(redux 自己的 dispatch 函数)
  4. // action 表示:要分发的动作
  5. const middle = store => {
  6. return next => {
  7. return action => {
  8. // 中间件代码写在这个位置:
  9. }
  10. }
  11. }
  12. // 简写
  13. const middle = store => next => action => {}

落地代码:

  1. // 简化写法:
  2. // store 表示:redux 的 store
  3. // next 表示:下一个中间件,
  4. // 如果只使用一个中间,那么 next 就是 store.dispatch(redux 自己的 dispatch 函数)
  5. // action 表示:要分发的动作
  6. const logger = store => next => action => {
  7. // 更新前的状态
  8. console.log('prev state:', store.getState())
  9. // 记录日志代码
  10. console.log('dispatching', action)
  11. // 如果只使用了一个中间件:
  12. // 那么,next 就表示原始的 dispatch
  13. // 也就是:logger中间件包装了 store.dispatch
  14. let result = next(action)
  15. // 上面 next 代码执行后,redux 状态就已经更新了,
  16. // 所以,再 getState() 拿到的就是更新后的最新状态值
  17. // 记录日志代码
  18. console.log('next state', store.getState()) // 更新后的状态
  19. return result
  20. }

7. 理解 Redux 异步数据流

目标:能够说出 redux 的异步数据流动过程

redux 异步.gif