如何封装一个分页器组件
文章最后会提供源码
分析与代码部分
需要分页器的原因:从服务器中获取的数据很多,如果全部一次性加载会导致卡顿,用户体验差
我们写一个分页器需要的外部数据(即使用该分页器组件时需要通过props传入的数据):
-
当前是第几页
pageNo
-
每一页展示多少数据
pageSize
-
整个分页器一共有多少数据
total
-
分页器连续页码个数
continues
(通常是奇数5 / 7,一般要求对称)
ps:其中暗含一个隐藏数据:整个分页器一共有多少页 Math.ceil(total / pageSize)
下面开始写代码:
计算连续页码的起始数字与结束数字
连续页码就是分页器中按数字顺序连续显示的页码,个数为 continues
例如上图中的 3、4、5、6、7 这5个页码,我们要先计算出这几个连续页码的开始页码和结束页码
逻辑:当前页码为第n页,假设continues = 5,那么起始数字为n-2,结束数字为n+2
// computed中
totalPage() {
return Math.ceil(this.total / this.pageSize);
},
// 连续页码起始数字与结束数字
startNumAndEndNum() {
const { pageNo, totalPage, continues } = this;
let start = 0;
let end = 0;
// 如果总页数少于连续页码数,只展示 1~最后页码数 即可
if (totalPage < continues) {
start = 1;
end = totalPage;
// 如果总页数大于等于连续页码数
} else {
start = pageNo - Math.floor(continues / 2);
end = pageNo + Math.floor(continues / 2);
// start为 0 / 负数
if (start < 1) {
start = 1;
end = continues;
}
// end大于总页数
if (end > totalPage) {
start = totalPage - continues + 1;
end = totalPage;
}
}
return {start, end};
},
连续页码数组
// computed中
pagesArr() {
let arr = [];
let pageNum = this.startNumAndEndNum.start;
arr.length = (this.totalPage < this.continues ? this.totalPage : this.continues);
for (let i = 0; i < arr.length; i++) {
arr[i] = pageNum;
pageNum++;
}
return arr
}
渲染数据
<ul>
<li class="prev disabled">
<a href="#">« 上一页</a>
</li>
<li v-show="startNumAndEndNum.start > 1">
<a href="#">1</a>
</li>
<li class="dotted" v-show="startNumAndEndNum.start > 2"><span>...</span></li>
<!-- 中间部分 -->
<li v-for="(p, index) in pagesArr" :key="index">
<a href="#">{{p}}</a>
</li>
<li class="dotted" v-show="startNumAndEndNum.end < totalPage - 1"><span>...</span></li>
<li v-show="startNumAndEndNum.end < totalPage">
<a href="#">{{ totalPage }}</a>
</li>
<li class="next">
<a href="#">下一页 »</a>
</li>
</ul>
完整代码:
<template>
<div class="fr page">
<div class="sui-pagination clearfix">
<ul>
<li
class="prev"
:class="{ disabled: pageNo === 1 }"
@click="pageNo > 1 ? $emit('getPageNo', pageNo - 1) : null"
>
<a>« 上一页</a>
</li>
<li
v-show="startNumAndEndNum.start > 1"
:class="{ active: pageNo === 1 }"
@click="$emit('getPageNo', 1)"
>
<a>1</a>
</li>
<li class="dotted" v-show="startNumAndEndNum.start > 2">
<span>...</span>
</li>
<!-- 中间部分 -->
<li
v-for="(page, index) in pagesArr"
:key="index"
:class="{ active: pageNo === page }"
@click="$emit('getPageNo', page)"
>
<a>{{ page }}</a>
</li>
<li class="dotted" v-show="startNumAndEndNum.end < totalPage - 1">
<span>...</span>
</li>
<li
v-show="startNumAndEndNum.end < totalPage"
:class="{ active: pageNo === totalPage }"
@click="$emit('getPageNo', totalPage)"
>
<a>{{ totalPage }}</a>
</li>
<li
class="next"
:class="{ disabled: pageNo === totalPage }"
@click="pageNo < totalPage ? $emit('getPageNo', pageNo + 1) : null"
>
<a>下一页 »</a>
</li>
</ul>
<div>
<span>共 {{ total }} 条</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Pagination",
props: ["pageNo", "pageSize", "total", "continues"],
computed: {
totalPage() {
return Math.ceil(this.total / this.pageSize);
},
// 连续页码起始数字与结束数字
startNumAndEndNum() {
const { pageNo, continues, totalPage } = this;
let start = 0;
let end = 0;
// 如果总页数少于连续页数
if (totalPage < continues) {
start = 1;
end = totalPage;
} else {
start = pageNo - Math.floor(continues / 2);
end = pageNo + Math.floor(continues / 2);
// start出现0/负数
if (start < 1) {
start = 1;
end = continues;
}
// end大于总页数
if (end > totalPage) {
start = totalPage - continues + 1;
end = totalPage;
}
}
return { start, end };
},
pagesArr() {
let arr = [];
// 连续页码个数
arr.length =
this.totalPage < this.continues ? this.totalPage : this.continues;
// 第一个连续页码
let pageNum = this.startNumAndEndNum.start;
for (let i = 0; i < arr.length; i++) {
arr[i] = pageNum;
pageNum++;
}
return arr;
},
},
};
</script>
<style scoped lang="less">
.page {
display: flex;
justify-content: center;
overflow: hidden;
.sui-pagination {
margin: 18px 0;
ul {
margin-left: 0;
margin-bottom: 0;
vertical-align: middle;
float: left;
li {
line-height: 18px;
display: inline-block;
a {
position: relative;
float: left;
background-color: #fff;
padding: 9px 18px;
border: 1px solid #e0e9ee;
margin-left: -1px;
line-height: 18px;
text-decoration: none;
font-size: 14px;
color: #333;
cursor: pointer;
}
&.active {
a {
background-color: #e1251b;
color: #fff;
cursor: default;
}
}
&.prev {
a {
margin-left: 0px;
background-color: #fafafa;
}
}
&.disabled {
a {
color: #999;
cursor: default;
}
}
&.dotted {
span {
position: relative;
float: left;
padding: 9px 18px;
border: 1px solid #e0e9ee;
margin-left: -1px;
background-color: #fff;
font-size: 14px;
line-height: 18px;
text-decoration: none;
color: #333;
}
}
&.next {
a {
background-color: #fafafa;
}
}
}
}
div {
margin-top: 10px;
margin-left: 20px;
color: #333;
font-size: 14px;
float: left;
width: 80px;
}
}
}
</style>
在其他组件中使用:
<!-- 分页器 -->
<Pagination
// 引号内写入想传入的数据
:pageNo=""
:pageSize=""
:total=""
:continues=""
@getPageNo=""
/>