本文是使用 Typescript 开发类 antd 分页器,并发布 npm 的第二篇,因为最近在业务中使用了 antd 中的 Pagination 开发了相关业务,而自己又对组件的封装由很有兴趣,所以打算用 ts 来封装一个具有 antd 所有功能的 Pagination。相信你也能轻轻松松的发布一个 npm 组件了。
相关系列文章
从零开始实现类 antd 分页器(一):搭建项目架构
从零开始实现类 antd 分页器(二):分页核心代码
从零开始实现类 antd 分页器(三):发布npm
写作过程中有些方法的具体实现没有讲到,大家可以自行查看源码。本案例项目仓库:
闲来无事,造个轮子 —— darrell-wheels
为了写作方便,antd 的 分页器我统一以 antd-pagination 代替,自己的分页器统一以 my-pagination 代替。
效果图
基础 + 更多
跳转页数 [ showQuickJumper ] + 改变页数 [ showSizeChanger ]
迷你 [ size: 'small' ]
简洁 [ simple: true ]
显示总数 [ showTotal ]
修改上一步和下一步相应文字 [ itemRender ]
分页逻辑
这里面的逻辑是实现这个分页器最最关键的地方,同时也是分页器的难点所在。
我们要计算出每一页的页码排布情况,什么时候该显示省略号,什么时候改成全部显示等等。
antd 的分页
我试了一下 antd-pagination 的页码显示,假设有 30 页,它的页码是如下分布的,我们以 allPages 代表总页数,current 代表当前是第几页:
// allPages = 30
当 current = 1, 显示 1 2 3 4 5 ... 30
当 current = 2, 显示 1 2 3 4 5 ... 30
当 current = 3, 显示 1 2 3 4 5 ... 30
当 current = 4, 显示 1 2 3 4 5 6 ... 30
当 current = 5, 显示 1 ... 3 4 5 6 7 ... 30
...
当 current = 13, 显示 1 ... 11 12 13 14 15 ... 30
当 current = 14, 显示 1 ... 12 13 14 15 16 ... 30
当 current = 15, 显示 1 ... 13 14 15 16 17 ... 30
当 current = 16, 显示 1 ... 14 15 16 17 18 ... 30
...
当 current = 26, 显示 1 ... 24 25 26 27 28 ... 30
当 current = 27, 显示 1 ... 25 26 27 28 29 30
当 current = 28, 显示 1 ... 26 27 28 29 30
当 current = 29, 显示 1 ... 26 27 28 29 30
当 current = 30, 显示 1 ... 26 27 28 29 30
复制代码
在 antd-pagination 有一个参数 showLessItems,当它为 true 时,意思是当前页面 左右显示各 1 个,为 false 时,代表当前页面 左右各显示 2 个。在这里我们把 当前页面 左右显示几个 暂且记为 pageBufferSize,
也就是说,antd-pagination 的 pageBufferSize 只有两个值 1 和 2,不过两个已经完全够了。
上面的例子,pageBufferSize = 2,及 showLessItems 为 false。
找规律
接着观察上面这一组数据,我们可以找一下规律,粗粗一看,我们会发现:
第 1 页 到 第 3 页 显示的页码是一样的,只有 一个后面省略号
第 4 页 比 前 3 页 多了一个页码,但是还是只有 一个后面省略号(临界点)
第 5 页 开始,到 第 26 页 出现了 两个省略号,并且中间都是 5 个
第 27 页 跟第四页类似 又回到了 一个前面省略号(临界点)
第 28 页 到 第 30 页,显示的页面一样,并只有 一个前面省略号。
代码实现
我们在这里先暂时不考虑复杂的 dom 输出,只使用简单的字符串输出。
这里笔者想到两种思路:
第一种思路
简单粗暴:就是把用代码翻译上面我们发现的规律,我们可以用 allPages、current、pageBufferSize 来表示:
// 临界点
当 current = 5,转化为代码 1 + pageBufferSize * 2
当 current = 26,转化为代码 allPages - pageBufferSize * 2
// 在临界点 区域内,都是双省略号
current >= 1 + pageBufferSize * 2 && current <= allPages + pageBufferSize * 2
// 然后在对 第4页 和 第27页做一下特殊处理
// 这两页的页码虽然只有一个省略号,但是比正常的会多出一个页码
当 current = 4,转化为代码 pageBufferSize * 2
当 current = 27,转化为代码 allPages - pageBufferSize * 2 + 1
// 接下来就是最简单的
当 current = 1 || 2 || 3,转化为代码 < pageBufferSize * 2 - 1
当 current = 28 || 29 || 30,> allPages - pageBufferSize * 2 + 1
复制代码
第二种思路
我们主要来讲一下第二种思路,这个也是 antd-pagination 内使用的方法,我们接下来的方法也只满足 pageBufferSize 为 1 或者 2 。
我们定义一个函数 showPages 方法来实现相关的逻辑;
/**
* 输出每一页 显示的页码 的 字符串
* @param current:当前页码;
* @param allPages:总页数;
* @param pageBufferSize:页数左右两边显示几个(1个 或 2个)
*/
function showPages (current, allPages, pageBufferSize){
let str = '';
...
// 待完成的逻辑
...
return str.trim();
}
// 总页数
let total = 30;
// 循环输出每页的页码字符串
for (let i = 1; i <= total; i++) {
console.log(showPages(i, total, 2));
}
复制代码
首先 antd-pagination 当总页数小于等于 5 + 2 * pageBufferSize 的时候,不管 current 是 第几页,所有的页码都会展现,我们可以在 showPages 添加如下代码:
// 当总页数 <= 5 + 2 * pageBufferSize
function showPages (current, allPages, pageBufferSize){
if (allPages <= 5 + pageBufferSize * 2) {
for (let i = 1; i <= allPages; i++) {
str = str + ' ' + i;
}
}
}
复制代码
此时我们设置 allPages = 8,在浏览器中我们可以看到如下图所示:
观察上面的字符串我们发现,当有两个省略号的时候,前面字符肯定是 1 ...,最后两个字符肯定是 ... 30。
1. 先循环输出 当前页码 与 页码前后 pageBufferSize 个数
我们在代码里面定义一个 left,代表字符串循环从这个数开始;再定义 right,代表字符串循环到此结束。left 的值根据前几页的 current 和 pageBufferSize ,我们很简单的可以得到。
let left = Math.max(1, current - pageBufferSize);
复制代码
同理可以得到 right 的值:
let right = Math.min(current + pageBufferSize, allPages);
复制代码
因为 前三页 和 后三页 都是显示 5 个数的,所以在这种情况下,我们要对 left 和 right 在根据 pageBufferSize 做一个调整:
if (current - 1 <= pageBufferSize) {
right = 1 + pageBufferSize * 2;
}
if (allPages - current <= pageBufferSize) {
left = allPages - pageBufferSize * 2;
}
复制代码
接着我们便可以循环输出了:
for (let i = left; i <= right; i++) {
str = str + ' ' + i;
}
复制代码
2. 然后再去拼相关的省略号
接下来我们就要给上面的 str 拼省略号了。
这这我们在第一种思路里面讲过,我们可以很快的写出如下代码:
if (current - 1 >