前端开发中,请求状态管理一直是代码量的主要来源。本文以分页列表为切入点,对比纯 Axios 和 alova 两种实现方式,分析请求策略化模式在减少样板代码方面的效果与适用边界。
典型场景:分页列表的两种实现
先看一个最常见的需求:从后端获取用户列表,支持分页。
方案一:基于 Axios 的常规实现
const [data, setData] = useState([]);
const [page, setPage] = useState(1);
const [pageSize] = useState(10);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchUsers = async (currentPage) => {
setLoading(true);
setError(null);
try {
const res = await axios.get('/api/users', {
params: { page: currentPage, pageSize: 10 },
});
setData(res.data.list);
setTotal(res.data.total);
} catch (e) {
setError(e.message);
} finally {
setLoading(false);
}
};
useEffect(() => { fetchUsers(page); }, [page]);
// 翻页处理
const handlePageChange = (newPage) => { setPage(newPage); };
这段代码包含了 5 个 state 声明、1 个 useEffect、1 个 try/catch 和 1 个翻页函数。核心业务逻辑——GET /api/users?page=1&pageSize=10——仅占了其中一行。
方案二:基于 alova 的 usePagination
const {
data, total, loading, error,
page, pageSize, nextPage, prevPage,
} = usePagination(
(page, pageSize) => alovaInstance.Get('/api/users', {
params: { page, pageSize },
}),
{ page: 1, pageSize: 10 }
);
两种写法的功能完全等价,但代码量从约 30 行(即使省略了翻页 UI 渲染部分)下降到 6 行。
差异分析:被减少的代码去了哪里?
对比两份代码,第一版中消失的部分是:
| 原 Axios 版本的组成 | alova 中的处理方式 |
|---|---|
loading 状态声明 + 开关 | usePagination 内部自动管理 |
error 状态声明 + 捕获 | usePagination 内部自动捕获 |
data 状态声明 + 赋值 | hook 返回值直接提供 |
page 状态 + 翻页函数 | hook 返回值内置 nextPage / prevPage |
total 状态 | hook 自动从响应中提取 |
| useEffect 依赖管理 | hook 内部处理 |
这些被移除的代码有一个共同特征:它们属于请求场景的"通用基础设施",与具体业务无关。在传统方案中,这些基础设施在每个组件中都需要重新声明一次,形成了高比例的样板代码。
alova 的做法本质上是一种场景封装:将"分页请求"这个完整模式——包括数据获取、状态管理、翻页控制——整体抽象为一个声明式 hook。开发者只需描述"请求什么"和"初始参数",其余由 hook 接管。
扩展到其他请求场景
alova 的策略 hook 覆盖了前端开发中常见的请求模式:
useRequest — 基础请求
// 组件挂载时自动请求
const { data, loading, error } = useRequest(getUserList());
// 手动触发:点击按钮后发送
const { send } = useRequest(createOrder, { immediate: false });
useWatcher — 响应式请求
// keyword 变化时自动重新请求,内置防抖
const { data } = useWatcher(
() => searchApi(keyword.value),
[keyword],
{ debounce: 300 }
);
useForm — 表单提交
const {
form, loading,
send: submit,
reset,
} = useForm(submitApi, {
initialForm: { name: '', email: '' },
});
每个 hook 的返回值结构是统一的——都包含 data、loading、error——同时在统一接口之上提供场景专属的能力。
六个场景的代码量对比:
| 场景 | Axios 代码行数 | alova 代码行数 | 减少比例 | 使用的 Hook |
|---|---|---|---|---|
| 分页列表 | ~30 行 | 6 行 | ~80% | usePagination |
| 搜索防抖 | ~25 行 | 3 行 | ~88% | useWatcher |
| 表单提交 | ~30 行 | 3 行 | ~90% | useForm |
| 轮询刷新 | ~25 行 | 3 行 | ~88% | useAutoRequest |
| 链式依赖 | ~30 行 | 5 行 | ~83% | useSerialRequest |
| 文件上传 | ~35 行 | 8 行 | ~77% | useUploader |
注:行数基于简化后的核心逻辑统计,不含 UI 渲染代码和 import 语句。
适用场景与局限
alova 适合的情况:
- 项目中存在大量"页面加载 → 发起请求 → 展示数据"的常规页面
- 需要统一管理请求状态的项目(loading、error、数据一致性)
- 频繁使用分页、搜索防抖、表单提交等标准化交互模式
- 希望减少团队内请求代码风格差异的项目
alova 可能不太适合的情况:
- 项目规模较小,请求逻辑简单,引入额外依赖的收益有限
- 已经深度使用了 React Query、SWR 等成熟方案的团队,迁移成本需重点评估
- 对请求行为有高度定制需求,且与 hook 预设行为不匹配的场景
- 非 React/Vue/Svelte 的框架生态(需确认支持的框架列表)
其他考量:
- 学习成本:与直接使用 Axios 相比,需要理解"策略 hook"的概念和各个 hook 的行为差异
- 调试透明度:hook 封装了状态管理,在出现非预期行为时,排查可能比直接操作 state 多一层抽象
- 迁移方式:alova 的 Method 创建 API(
Get、Post等)与 Axios 高度相似,可以逐步引入,无需全量重写
// 两者的 Method 创建语法几乎相同
axios.get('/api/users', { params: { page: 1 } });
alovaInstance.Get('/api/users', { params: { page: 1 } });
小结
从 Axios 到 alova,核心变化不在于"换了一个请求库",而在于将请求视为可被完整封装的场景单元,而非一个需要手动管理所有副作用的原子操作。这种抽象层次的提升,是代码量显著减少的根本原因。
对于正在使用传统请求方案的项目,alova 提供了一种减少请求样板代码的可选路径。是否采用,取决于项目的具体需求、团队已有的技术栈和迁移成本的综合权衡。