Pagination分页组件的实现细节与性能优化实践
我的写法,亲测靠谱
分页这个功能,我写了不下几十遍了。说实话,一开始我也踩过不少坑,但经过几个项目的折腾,总结出一套自己觉得还不错的写法。核心代码其实不复杂,重点是思路要清晰。
下面是我常用的Pagination组件代码:
function Pagination({ total, pageSize, currentPage, onPageChange }) {
const totalPages = Math.ceil(total / pageSize);
const range = 2; // 当前页前后显示的页码数
if (totalPages <= 1) return null;
const pages = [];
for (let i = Math.max(1, currentPage - range); i <= Math.min(totalPages, currentPage + range); i++) {
pages.push(i);
}
return (
<ul className="pagination">
{currentPage > 1 && (
<li onClick={() => onPageChange(currentPage - 1)}>上一页</li>
)}
{pages[0] > 1 && <li onClick={() => onPageChange(1)}>1</li>}
{pages[0] > 2 && <li>...</li>}
{pages.map(page => (
<li
key={page}
className={page === currentPage ? 'active' : ''}
onClick={() => onPageChange(page)}
>
{page}
</li>
))}
{pages[pages.length - 1] < totalPages - 1 && <li>...</li>}
{pages[pages.length - 1] < totalPages && (
<li onClick={() => onPageChange(totalPages)}>{totalPages}</li>
)}
{currentPage < totalPages && (
<li onClick={() => onPageChange(currentPage + 1)}>下一页</li>
)}
</ul>
);
}
为什么这样写?首先,我把总条目数、每页显示数量、当前页和页码变更回调都作为props传入,这样组件的复用性就很强。其次,我用了range来控制显示的页码范围,这个值可以根据需求调整。
这几种错误写法,别再踩坑了
说真的,我在项目里见过太多分页的奇葩写法,有些甚至让我怀疑人生。最常见的错误有这么几种:
- 直接在每个页面都写死分页逻辑,导致到处都是重复代码
- 把所有页码都渲染出来,结果数据量大的时候直接卡死
- 没有处理边界情况,比如第一页的时候还能点”上一页”,最后一页还能点”下一页”
举个例子,之前接手一个项目,看到这样的代码:
// 反面教材,请勿模仿
for (let i = 1; i <= totalPages; i++) {
pageButtons += <button>${i}</button>;
}
这代码看着简单,但当totalPages很大的时候,性能直接爆炸。而且完全没有考虑用户体验,页面上可能密密麻麻几百个按钮。
实际项目中的坑
在真实项目里,分页往往会遇到各种意想不到的问题。这里分享几个我踩过的坑:
第一个坑是API设计问题。有些后端接口要求传offset和limit,有些要求传pageNumber和pageSize,还有些直接要传起始ID和结束ID。我就遇到过一个特别离谱的情况:
// 后端要求的奇葩参数格式
fetch(https://jztheme.com/api/data?from=${(currentPage-1)*pageSize}&size=${pageSize})
这种写法不仅容易算错,而且可读性极差。后来我和后端商量,统一改成pageNumber和pageSize,代码立马清爽很多。
第二个坑是数据更新不同步。有时候用户快速点击分页按钮,可能会导致请求交错返回,出现数据错乱的情况。我的解决方法是加了个loading状态:
const [loading, setLoading] = useState(false);
const handlePageChange = async (page) => {
if (loading) return;
setLoading(true);
await fetchData(page);
setLoading(false);
};
第三个坑是样式适配问题。尤其是移动端,如果页码太多会撑爆屏幕。所以我一般会给页码容器设置最大宽度,并加上overflow: auto。
一些小技巧分享
除了上面提到的核心逻辑,我还总结了一些实用的小技巧:
- 给当前页加个明显的样式标识,比如加粗或者换个颜色
- 当总页数超过一定数量时,自动隐藏部分页码,只保留首尾和当前页附近的页码
- 记得处理键盘事件,让用户可以通过方向键切换分页
- 添加防抖处理,防止用户疯狂点击分页按钮
这里有个小细节,我发现很多人忽略:当用户从搜索结果跳转到详情页,再返回列表页时,应该记住之前的分页状态。这个可以通过简单的状态管理或者URL参数来实现。
以上是我总结的最佳实践
分页看似简单,但要做好还是挺考验功底的。从我的经验来看,关键是要考虑全面:性能、用户体验、边界情况、特殊场景都要照顾到。
当然,我这套方案也不是完美无缺的,比如在超大数据量的情况下,可能需要更复杂的虚拟滚动方案。不过对于大多数业务场景来说,已经够用了。
如果你有更好的实现方式,或者遇到了什么奇葩的分页需求,欢迎在评论区交流。毕竟前端开发就是这样,总能遇到新花样,大家一起进步嘛。

暂无评论