redux-thunk 和 redux-saga
在 Redux 中,中间件(Middleware)用于处理副作用(如异步处理,日志记录等)。redux-thunk 和 redux-saga 是最常用的两种处理异步的中间件(Middleware),他们在设计思路、使用方法和适用场景上有显著差异。
信息
redux-thunk 是”够用就好“的务实选择,而 redux-saga 是为了解决复杂问题而生的强大工具
一、 redux-thunk
1. 核心思想
redux-thunk 允许 action creator 返回一个函数(而不仅仅是一个对象),这个函数可以包含异步逻辑,并通过 dispatch 主动触发其他 action。本质是将异步逻辑封装在 action creator 中。
2. 安装和引入
npm install redux-thunk
在 Redux Store 中应用中间件:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware);
3. 编写异步 Action Creator
返回一个函数,接收 dispatch 和 getState 作为参数:
// 普通 Action (同步)
const fetchUserSuccess = user => ({
type: 'FETCH_USER_SUCCESS',
payload: user,
});
const fetchUserFailure = error => ({
type: 'FETCH_USER_FAILURE',
payload: error,
});
// 异步 action (使用 thunk)
const fetchUser = userId => (dispatch, getState) => {
// 先 dispatch 加载状态
dispatch({ type: 'FETCH_USER_PENDING' });
// 模拟异步请求
fetch(`https://api.lmssee.com/users/${userId}`)
.then(res => res.json())
.then(user => dispatch(fetchUserSuccess(suer)))
.catch(error => dispatch(fetchUserFailure(error)));
};
// 触发 异步 action
store.dispatch(fetch(123));
4. 特点
- 简单轻量:仅需封装函数,学习成本低
- 逻辑分散:异步逻辑直接写在 action creator 中,可能导致组件或 action 文件臃肿
- 灵活性低:难以处理复杂流程(如竞态条件、取消请求、并发控制)
二、 redux-saga
redux-saga 使用 生成器函数(Generator)来管理副作用,通过监听特定 action 触发任务,将异步逻辑集中到独立的 saga 文件中,实现更可控的副作用管理。
1. 安装与配置
- 安装
- 配置
npm install redux-saga
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './reducers';
import rootSage from './sagas';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga); // 启动 saga
2. 编写 Saga 任务
使用生成器函数定义任务。通过 yield 执行异步操作或派发 action:
import { takeEvery, put, call } from 'redux-sage/effects';
// 监听 FETCH_USER_REQUEST 动作
function* fetchUserSaga(action) {
try {
// 调用异步函数 (call effect)
const user = yield call(
fetch,
`https://api.lmssee.com/user/${action.payload}`,
);
const data = yield user.json();
// 派发成功 action (put effect 等价于 dispatch )
yield put({ type: 'FETCH_USER_SUCCESS', payload: data });
} catch (error) {
// 派发失败 action
yield put({ type: 'FETCH_USER_FAILURE', payload: error });
}
}
// 监听所有 FETCH_USER_REQUEST 动作(类似事件监听)
function* watchFetchUser() {
yield takeEvery('FETCH_USER_REQUEST', fetchUserSage);
}
// 根 sage (组合所有任务)
export default function* rooSaga() {
yield watchFetchUser();
// 添加其他任务,如 watchAnotherAction();
}
3. 触发动作
组件中只需要 dispatch 普通 action:
store.dispatch({ type: 'FETCH_USER_REQUEST', payload: 123 });
4. 核心概念
- Effect:redux-saga 通过 effect 描述副作用(如
call调用异步函数,put派发 action,takeEvery监听 action),生成器通过yield执行这些 Effect - 生成器函数: 通过
function*定义,yield执行暂停,等待异步操作完成 - 任务组合:可以通过
fork并行任务,race竞态条件,takeEvery取消旧任务等
三、核心区别
| 纬度 | redux-thunk | redux-saga |
|---|---|---|
| 实现方式 | 函数返回函数(闭包) | 生成器函数 + Effect 描述 |
| 异步逻辑位置 | 封装在 action creator 中 | 集中在独立的 saga 文件中 |
| 复杂度 | ✅ 简单,学习成本低 | ❌ 较复杂,需要理解生成器和 Effect |
| 流程可控 | ❌ 有限(难以处理竞态、取消等) | ✅ 强大(支持 race、takeLatest、cancel 等 |
| 并发处理 | ❌ 手动管理(Promise.all 等) | ✅ 内置强大支持(race、fork、all 等) |
| 可测试性 | ❌ 需 mock dispatch 和 getState | ✅ 生成器输出纯值,易测试(无需真实测试) |
| 调试 | ✅ 相对简单,逻辑线形 | ❌ 较复杂,但有 DevTools 支持 |
| 代码体积 | ✅ 小(< 1KB) | ❌ 较大 |
| 适用场景 | 简单异步(如单个 API 调用) | 复杂流程(如并发、取消、长轮询、状态机) |
四、使用场景
1.选择 redux-thunk
- 异步逻辑简单(如单个 API 请求,无复杂依赖)
- 希望快速上手,避免额外学习成本
- 项目规模小,不需要集中管理副作用
- 项目追求快速开发和最小化依赖
- 习惯使用
async/await
2.选择 redux-saga
- 需要处理复杂异步流程(如竞态条件:同时发起多个请求,只取最后一个结果)
- 需要取消未完成的请求(如用户离开页面时取消 API 调用)
- 异步逻辑需要复用或集中管理(如多个组件触发同一异步任务)
- 需要持久化副作用任务(如页面刷新后恢复任务)
- 项目规模大,需要更好的可维护性和可测试性
3. 现代选择
随着 React Hook 和 useReducer + useEffect 的普及,以及像 「RTK Query」(Redux Toolkit 自带)这样的现代化的解决方案出现,许多新项目已经不再使用 redux-thunk 和 redux-saga 。「RTK Query」提供了开箱即用、自动更新获取等功能,极大地简化了数据获取。但对于需要精细控制复杂副作用的场景。 redux-saga 依然有其不可替代的价值。