人人都会AI编程

Redux 中间件原理与常用中间件

更新时间:2026-07-11

在 Redux 中,数据流是严格单向且同步的:dispatch(action) → reducer → store。但在真实应用中,我们经常需要在派发动作和到达 reducer 之间插入额外逻辑,比如日志记录、异常上报、异步请求等。中间件(Middleware) 就是为这种需求设计的扩展点。

中间件的工作原理

中间件本质上是一个三层高阶函数,它拦截每一个被 dispatch 的 action,可以执行任意操作后决定是否将 action 传递给下一个中间件或 reducer。多个中间件可以链式组合,形成一个“洋葱圈”模型。

一个最简中间件的结构:

const exampleMiddleware = store => next => action => {
  // 前置处理:在 action 到达下一个中间件/reducer 之前执行
  console.log('即将处理 action:', action);

  // 调用 next(action) 让 action 继续向里传递
  const result = next(action);

  // 后置处理:在 reducer 处理完状态后执行
  console.log('处理之后的状态:', store.getState());

  return result;
};
  • store:提供了 dispatchgetState 方法,但通常不在中间件内部直接使用 store.dispatch,以免打乱中间件执行顺序。
  • next:下一个中间件的 dispatch 函数,如果当前是最后一个中间件,则 next 就是原生的 store.dispatch,最终会把 action 传入 reducer。
  • action:当前被 dispatch 的 action 对象。

通过 applyMiddleware(...) 可以将多个中间件串联起来:

import { createStore, applyMiddleware } from 'redux';

const store = createStore(
  rootReducer,
  applyMiddleware(middleware1, middleware2, middleware3)
);

dispatch 一个 action 时,会依次经过 middleware1 → middleware2 → middleware3 → reducer,每个中间件都可以在调用 next(action) 前后添加自己的逻辑。

常用中间件

1. redux-thunk

作用:允许 dispatch 一个函数(thunk)而不是普通的 action 对象,用于处理简单的异步逻辑。
原理:当检测到 action 是函数时,中间件会调用它并传入 dispatchgetState,由函数内部决定何时 dispatch 真正的 action。

// 安装:npm install redux-thunk
import thunk from 'redux-thunk';

// 创建一个异步 action creator
const fetchUser = (userId) => {
  return async (dispatch, getState) => {
    dispatch({ type: 'USER_REQUEST' });
    try {
      const user = await fetch(`/api/users/${userId}`).then(res => res.json());
      dispatch({ type: 'USER_SUCCESS', payload: user });
    } catch (error) {
      dispatch({ type: 'USER_FAILURE', error });
    }
  };
};

// 在组件中直接 dispatch 函数
store.dispatch(fetchUser(1));

适用场景:简单的数据获取、条件判断延迟派发等。适合中小型项目,代码简洁。

2. redux-saga

作用:使用 Generator 函数让异步流程管理更清晰、可测试,适合复杂异步场景。
原理:Saga 中间件启动后,监听被 dispatch 的特定 action,然后执行对应的 Generator 函数。这些函数内部使用声明式的 Effect(如 callputtakeLatest)描述副作用,中间件负责执行并控制流程。

import createSagaMiddleware from 'redux-saga';
import { call, put, takeLatest } from 'redux-saga/effects';

// 创建中间件实例
const sagaMiddleware = createSagaMiddleware();

// 应用到 store
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));

// 定义 worker saga
function* fetchUser(action) {
  yield put({ type: 'USER_REQUEST' });
  try {
    const user = yield call(fetch, `/api/users/${action.payload}`);
    const data = yield user.json();
    yield put({ type: 'USER_SUCCESS', payload: data });
  } catch (e) {
    yield put({ type: 'USER_FAILURE', error: e.message });
  }
}

// 定义 watcher saga
function* mySaga() {
  yield takeLatest('FETCH_USER', fetchUser);
}

// 启动 saga
sagaMiddleware.run(mySaga);

优势:异步流程被写成看似同步的代码,易于阅读;各种 Effect 可以精确控制和测试(无需模拟异步);内置 takeLatestthrottle 等高级并发控制。
适用场景:复杂的长流程异步操作(如多个依赖请求、WebSocket 重连、竞态处理),以及需要精细控制副作用的项目。

3. redux-observable

作用:使用 RxJS 的强大流管理能力处理异步,适合以事件流为思想的复杂 UI 交互。
原理:中间件监听 action 流,将 action 看作事件,Epic(一个函数)接收 action 流和 state 流,返回一个新的 action 流。中间件自动订阅并 dispatch 返回的 action。

import { createEpicMiddleware } from 'redux-observable';
import { ajax } from 'rxjs/ajax';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { ofType } from 'redux-observable';

const epicMiddleware = createEpicMiddleware();

// Epic 函数
const fetchUserEpic = (action$, state$) => action$.pipe(
  ofType('FETCH_USER'),
  mergeMap(action =>
    ajax.getJSON(`/api/users/${action.payload}`).pipe(
      map(user => ({ type: 'USER_SUCCESS', payload: user })),
      catchError(error => of({ type: 'USER_FAILURE', error }))
    )
  )
);

// 应用并运行
const store = createStore(rootReducer, applyMiddleware(epicMiddleware));
epicMiddleware.run(fetchUserEpic);

优势:充分利用 RxJS 的操作符轻松实现防抖、节流、取消、竞态处理等;适合事件驱动的复杂交互。
适用场景:具备 RxJS 经验且项目中交互高度复杂(如实时搜索、拖拽、WebSocket 流处理)。

4. redux-logger

作用:在控制台打印每次 action 的详细信息,便于开发和调试。
原理:简单的中间件,记录 action 名称、旧状态、新状态和耗时。

import { createLogger } from 'redux-logger';

const logger = createLogger({
  collapsed: true,  // 折叠打印
  duration: true,
});

// 通常放在中间件链的最后,保证日志记录的是最终传递给 reducer 的 action
applyMiddleware(thunk, sagaMiddleware, logger);

中间件选型建议

| 中间件 | 异步方式 | 学习曲线 | 典型场景 |
|--------|----------|----------|----------|
| redux-thunk | 回调/async 函数 | 低 | 简单数据请求、条件派发 |
| redux-saga | Generator + Effect | 中高 | 复杂异步流程、竞态控制、测试要求高 |
| redux-observable | RxJS 流 | 高(需掌握 RxJS) | 强事件流交互、已使用 RxJS 的项目 |

在现代 Redux 开发中,强烈推荐使用 Redux Toolkit (RTK),它内置了 createAsyncThunk(内部基于 thunk 但简化了样板代码)和 RTK Query(数据缓存与请求),能覆盖大部分异步场景,通常不再需要单独安装 redux-thunk 或 saga。仅在 RTK 无法满足的极复杂异步场景下才考虑引入 saga 或 observable。

中间件是 Redux 可扩展性的核心体现,理解它的设计能让你更好地掌控应用的数据流和副作用处理。