分页数据应由后端按 page 和 limit 返回,前端仅传参、渲染及管理状态;page 通常从 1 开始,limit 建议固定;响应需含 total 以计算总页数;优先使用成熟 UI 库分页组件,手写需处理边界;点击分页适合需跳转、SEO 场景,无限滚动适用于内容流;前端本地分页仅限小量静态数据。
page 和 limit 控制绝大多数分页不是前端“算出来”的,而是靠后端按页返回数据。前端只负责传参数、渲染、翻页状态。关键参数通常是 page(当前页码,一般从 1 开始)和 limit(每页条数)。后端根据这两个值查对应区间的数据。
常见错误是前端把 page 当成从 0 开始——比如第一页传 page=0,结果后端没匹配上,返回空数组或报错。务必确认接口文档约定。
page 通常为正整数,1 表示第一页limit 建议固定(如 10 或 20),避免用户随意输导致性能抖动total(总条数)、page、limit,方便前端算总页数:Math.ceil(total / limit)
纯手写分页按钮逻辑不难,但边界情况多:比如只有 1 页要不要显示?页码太多要不要省略(1 ... 5 6 7 ... 100)?移动端要不要折叠?这些细节容易反复踩坑。
如果项目已用 React 或 Vue,优先选生态内成熟组件,比如 Ant Design 的 Pagination 或 Element Plus 的 el-pagination。它们默认处理了禁用态、键盘导航、屏幕阅读器支持等。
若必须手写,核心是生成页码数组:
function generatePageNumbers(current, total, siblingCount = 1) {
const pages = [];
const totalPageCount = Math.ceil(total);
if (totalPageCount <= 6) {
for (let i = 1; i <= totalPageCount; i++) pages.push(i);
} else {
const leftSiblingIndex = Math.max(2, current - siblingCount);
const rightSiblingIndex = Math.min(totalPageCount - 1, current + siblingCount);
pages.push(1);
if (leftSiblingIndex > 2) pages.push('...');
for (let i = leftSiblingIndex; i <= rightSiblingIndex; i++) pages.push(i);
if (rightSiblingIndex < totalPageCount - 1) pages.push('...');
pages.push(totalPageCount);}
return pages;
}
注意:这个函数返回的是要显示的页码项(含 '...' 字符串),不是原始数据索引,别直接拿去当 page 参数发请求。
无限滚动 vs 点击分页:什么时候该选哪个?
“分页”不等于必须点数字按钮。两种主流交互方式适用场景差异明显:
?page=3&limit=10,刷新不丢状态别为了“酷”强行用无限滚动。电商商品搜索结果页如果不用传统分页,用户想找第 47 页的某款老型号,基本找不到。
只有当全部数据一次性加载完成(比如 100 条以内),且确定不会增删改,才考虑前端分页。典型场景:配置项列表、静态帮助文档目录。
否则极易出问题:

真要用,核心就是 slice:
const allData = [/* 一整个数组 */]; const currentPage = 3; const limit = 10; const startIndex = (currentPage - 1) * limit; const paginatedData = allData.slice(startIndex, startIndex + limit);
但请再确认一遍:你真的不需要服务端分页吗?大多数所谓“前端分页需求”,其实是没跟后端对齐接口设计。