1. 中间件概述
目标:能够理解为什么需要 redux 中间件
知识点:
- 默认情况下,Redux 自身只能处理同步数据流。但是在实际项目开发中,状态的更新、获取,通常是使用异步操作来实现。
- 问题:如何在 Redux 中进行异步操作呢 ?
- 回答:通过 Redux 中间件机制来实现 !
Redux 中间件作用:处理具有副作用(side effect)的功能,比如,异步操作就是最常见的 side effect
中间件说明:
- 中间件,可以理解为处理一个功能的中间环节
- 下图中,自来水从水库到用户家庭中的每一个环节都是一个中间件
- 中间件的优势:可以串联、组合,在一个项目中使用多个中间件
- Redux 中间件用来处理 状态 更新,也就是在 状态 更新的过程中,执行一系列的相应操作

总结:
默认情况下,Redux 自身只能处理同步数据流
在 Redux 中进行异步操作,需要通过 Redux 中间件机制来实现
2. 中间件的触发时机
目标:能够理解中间件的触发时机
知识点:
Redux 中间件执行时机:在 dispatching action 和 到达 reducer 之间。
- 没有中间件:dispatch(action) => reducer

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

- 原理:封装了 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中间件记录日志
使用步骤:
- 安装:yarn add redux-logger
- 导入 redux-logger 中间件
- 从 redux 中导入 applyMiddleware 函数
- 调用 applyMiddleware() 并传入 logger 中间件作为参数
- 将 applyMiddleware() 调用作为 createStore 函数的第二个参数
然后,调用 store.dispatch() 查看 console 中 logger 中间件记录的日志信息
落地代码:
// 导入 applyMiddleware 函数import { createStore, applyMiddleware } from 'redux'// 导入 logger 中间件import logger from 'redux-logger'import rootReducer from './reducers'// 将 applyMiddleware 方法传入第二个参数// 将 logger 中间件作为参数传入 applyMiddleware 方法const store = createStore(rootReducer, applyMiddleware(logger))
4. Redux 中间件原理
目标:了解中间件的实现原理
知识点:
Redux 中间件原理:创建一个函数,包装 store.dispatch,使用新创建的函数作为新的 dispatch
- 比如下图,logger 就是一个中间件,使用该中间件后 store.dispatch 就是包装后的新 dispatch
- 中间件修改了 store.dispatch,在分发动作和到达 reducer 之间提供了扩展

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

5. Redux 中间件原理代码演示
目标:通过代码的方式加深对 redux 中间件的理解
落地代码:
<!DOCTYPE html><html><head><script src="https://cdn.bootcdn.net/ajax/libs/redux/5.0.0-alpha.0/redux.min.js"></script></head><body><script>function middleware1(store) {return function (next) {return function (action) {console.log('A middleware1 开始')next(action)console.log('B middleware1 结束')}}}function middleware2(store) {return function (next) {return function (action) {console.log('C middleware2 开始')next(action)console.log('D middleware2 结束')}}}function middleware3(store) {return function (next) {return function (action) {console.log('E middleware3 开始')next(action)console.log('F middleware3 结束')}}}function reducer(state, action) {if (action.type === 'middleware') {console.log('-----------------')console.log('- 华丽丽的分隔线-')console.log('-----------------')}return {}}var store = Redux.createStore(reducer,Redux.applyMiddleware(middleware1,middleware2,middleware3))store.dispatch({ type: 'middleware' })</script></body></html>
控制台输出
middleware1 开始middleware2 开始middleware3 开始------------------ 华丽丽的分隔线------------------middleware3 结束middleware2 结束middleware1 结束
触发流程图:
--------------------------------------| middleware1 || ---------------------------- || | middleware2 | || | ------------------- | || | | middleware3 | | || | | | | |next next next ——————————— | | |dispatch —————————————> | reducer | — 收尾工作->|nextState <————————————— | G | | | || A | C | E ——————————— F | D | B || | | | | || | ------------------- | || ---------------------------- |--------------------------------------顺序 A -> C -> E -> G -> F -> D -> B\---------------/ \----------/↓ ↓更新 state 完毕 收尾工作
6. 手写 Redux-logger 中间件
目标:加深如 Redux 中间件原理的理解
案例分析:
中间件的固定格式
// store 表示:redux 的 store// next 表示:下一个中间件,// 如果只使用一个中间,那么 next 就是 store.dispatch(redux 自己的 dispatch 函数)// action 表示:要分发的动作const middle = store => {return next => {return action => {// 中间件代码写在这个位置:}}}// 简写const middle = store => next => action => {}
落地代码:
// 简化写法:// store 表示:redux 的 store// next 表示:下一个中间件,// 如果只使用一个中间,那么 next 就是 store.dispatch(redux 自己的 dispatch 函数)// action 表示:要分发的动作const logger = store => next => action => {// 更新前的状态console.log('prev state:', store.getState())// 记录日志代码console.log('dispatching', action)// 如果只使用了一个中间件:// 那么,next 就表示原始的 dispatch// 也就是:logger中间件包装了 store.dispatchlet result = next(action)// 上面 next 代码执行后,redux 状态就已经更新了,// 所以,再 getState() 拿到的就是更新后的最新状态值// 记录日志代码console.log('next state', store.getState()) // 更新后的状态return result}
7. 理解 Redux 异步数据流
目标:能够说出 redux 的异步数据流动过程

