告别手写分页逻辑:usePagination 从 50 行到 3 行

摘要

在前端开发中,一个基础的分页列表通常需要维护 page、pageSize、total、loading、error 等多个状态,并编写翻页、筛选防抖、增删改操作等样板代码,实现量约 50-60 行。alova 的 usePagination Hook 将分页场景的模式抽象为声明式配置,核心代码压缩至约 3 行,同时提供相邻页预加载、列表项乐观更新、多粒度操作状态等手写方案容易忽略的能力。本文通过代码对比,分析 usePagination 的核心用法、配置项含义及适用边界。


一、手写分页的实现

以下是一个标准的分页列表手写实现(Vue 3 Composition API):

javascript 复制代码
// 状态声明 --- 6 个 ref
const list = ref([]);
const loading = ref(false);
const error = ref(null);
const page = ref(1);
const pageSize = ref(10);
const total = ref(0);
const searchKeyword = ref('');

// 请求函数 --- 手动管理 loading/error 切换
const fetchList = async () => {
  loading.value = true;
  error.value = null;
  try {
    const res = await axios.get('/api/users', {
      params: {
        page: page.value,
        pageSize: pageSize.value,
        keyword: searchKeyword.value,
      },
    });
    list.value = res.data.data;
    total.value = res.data.total;
  } catch (e) {
    error.value = e.message;
  } finally {
    loading.value = false;
  }
};

// 翻页 → 改 page → 手动 fetch
const changePage = (p) => {
  page.value = p;
  fetchList();
};

// 每页条数变化 → 重置 page → 手动 fetch
const changePageSize = (ps) => {
  pageSize.value = ps;
  page.value = 1;
  fetchList();
};

// 搜索 → 重置 page → 手动 fetch
const onSearch = (keyword) => {
  searchKeyword.value = keyword;
  page.value = 1;
  fetchList();
};

// 删除 → 调接口 → 手动 fetch
const deleteItem = async (id) => {
  await axios.delete(`/api/users/${id}`);
  fetchList();
};

onMounted(() => fetchList());

核心逻辑(GET /api/users)仅 1 行,其余 50+ 行都属于"请求基础设施"------状态声明、加载切换、翻页联动、筛选重置。这些代码在各个列表页面反复出现。

二、usePagination 的声明式方案

usePagination 将分页场景固化为 hook:

javascript 复制代码
import { usePagination } from 'alova/client';

const searchKeyword = ref('');

const {
  loading, data, error,
  page, pageSize, total, pageCount, isLastPage,
  fetching, removing, replacing, status,
  refresh, insert, remove, replace, reload,
  onSuccess, onError, onComplete,
} = usePagination(
  (page, pageSize) => alovaInstance.Get('/api/users', {
    params: { page, pageSize, keyword: searchKeyword.value },
  }),
  {
    initialPage: 1,
    initialPageSize: 10,
    watchingStates: [searchKeyword],
    debounce: 300,
  }
);

配置完成后,以下能力由框架接管:

翻页与每页条数变更

修改 pagepageSize 自动发起请求,pageSize 变更时自动重置为第1页:

javascript 复制代码
page.value = 3;       // 跳转到第3页,自动请求
pageSize.value = 20;  // 修改每页条数,自动重置第1页并请求

筛选条件防抖搜索

通过 watchingStates 监听筛选条件变化,结合 debounce 实现防抖:

javascript 复制代码
searchKeyword.value = '张三';  // 300ms 防抖后自动从第1页请求

列表项乐观操作

insertremovereplace 操作本地列表并同步服务端,无需手动 refresh:

javascript 复制代码
await insert({ id: 99, name: '新用户' }, 0);               // 在前面插入
await remove(2);                                              // 删除第3项
await replace({ id: 5, name: '更新后' }, 4);                // 替换第5项
await refresh(page.value);                                    // 强制刷新当前页
await reload();                                               // 清空数据,重新加载第1页

相邻页预加载

默认开启上一页和下一页的预加载。用户翻页时数据已从缓存读取,无需等待网络请求。

操作级状态

提供比单一 loading 更细粒度的状态:

javascript 复制代码
loading,    // 当前页请求中
fetching,   // 预加载请求中(不影响当前页 UI)
removing,   // 正在删除的行索引数组
replacing,  // 正在替换的行索引
status,     // 当前操作:"loading" | "removing" | "inserting" | "replacing"

便于行级操作时显示独立 loading,而不阻塞整个列表。

三、技术原理

代码量减少的根本原因在于抽象层次的提升:

  • 传统方案使用 Axios/fetch 处理单次 HTTP 请求,开发者需自行管理每次请求的全部状态
  • usePagination 将"分页列表请求"视为一个完整的业务场景,将通用逻辑(loading 切换、错误捕获、分页参数维护、翻页联动)内置到 hook 中
  • 各操作函数(insert/remove/replace)在乐观更新本地列表后自动同步服务端,减少手动 refresh

四、适用场景与局限

适用场景:

  • 标准 CRUD 管理页面:用户管理、订单管理、内容管理等需要分页、搜索、增删改的列表
  • 多条件筛选 + 分页组合:筛选条件变化自动重置页码的场景
  • 列表项频繁操作:行内编辑、删除、插入频繁,可利用乐观更新避免每次操作后重新请求
  • API 返回标准分页结构:{ data: [], total: number } 或可配置的结构

不适用场景:

  • 游标分页(Cursor-based):使用 after/before 游标的 API,页码模型无法直接映射
  • 双端无限滚动:同时支持上下加载更多(如聊天记录),usePagination 追加模式仅支持单向
  • 多列表联动:一个操作需同步更新多个分页列表且更新顺序有严格要求
  • 多接口数据聚合:列表数据需从多个接口拼装且无法通过 data/total 回调完成

其他考量:

  • 学习成本:需理解策略 hook 的概念和各配置项行为
  • 调试透明度:封装层可能增加底层请求行为的排查复杂度
  • 可定制性:受 hook 预设行为约束,极端定制场景需自行实现

五、总结

usePagination 本质上不是在替换 HTTP 库,而是将分页场景的通用模式固化为配置项,减少重复编码的同时引入预加载、操作级状态等手写方案容易忽略的能力。对于标准分页场景,其代码量和维护成本的减少是显著的;对于超出设计边界的场景,传统手写方案仍然更加灵活。


标签: alova, usePagination, 前端分页, Vue, React

作者简介: 爱编程的小金,前端工程师,关注前端工程化与请求架构优化。

相关推荐
触底反弹1 小时前
5 个 Step,让你的前端代码连上 AI 大模型
javascript·人工智能·面试
xiaofeichaichai1 小时前
Symbol 与 Iterator / Generator
前端·javascript
zzqssliu2 小时前
Taocarts库存锁定机制优化:彻底解决跨境代购商品超卖问题
java·linux·javascript·php
scan7242 小时前
SystemMessage,HumanMessage,AIMessage,ToolMessage
开发语言·前端·javascript
rising start2 小时前
二、Vue3 核心基础:API 对比、Setup 与响应式详解
前端·javascript·vue.js
meilindehuzi_a2 小时前
撕开 JS 的 Class 面具:从构造函数的 new 降生到顶层原型链的终极通关
开发语言·javascript·ecmascript
阿坨2 小时前
JavaScript异步编程:Promise与async/await
javascript
独特的螺狮粉3 小时前
蛋鸡养护周期管理系统 - 鸿蒙PC Electron框架完整实现指南
前端·javascript·华为·electron·前端框架·开源·鸿蒙
我穿棉裤了3 小时前
解决el-form表单校验时显示的红色星号与文字对齐的问题
前端·javascript·vue.js