在 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:提供了dispatch和getState方法,但通常不在中间件内部直接使用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 是函数时,中间件会调用它并传入 dispatch 和 getState,由函数内部决定何时 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(如 call、put、takeLatest)描述副作用,中间件负责执行并控制流程。
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 可以精确控制和测试(无需模拟异步);内置 takeLatest、throttle 等高级并发控制。
适用场景:复杂的长流程异步操作(如多个依赖请求、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 可扩展性的核心体现,理解它的设计能让你更好地掌控应用的数据流和副作用处理。