需求背景:数据量太大,按照正常的分页功能开发,服务端响应极其慢,效果不好。
预期结果:预期做成动态分页,点一页渲染一页,一直到最后一页
实现思路
首先是页面设计为如下,“下一页”文案可自行调整
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[5,6,7,10,20]"
:current-page.sync="current"
:page-size="size"
layout="sizes, prev, pager, slot"
:total="total"
>
<el-button type="text" @click="showMore">下一页</el-button>
</el-pagination>
showMore() {
// 当前页不是已经渲染的最后一页,点击下一页,不用动态计算
const isCalculate = this.current == this.maxPage;
this.handleCurrentChange(this.current + 1, isCalculate);
}
分析数据需要如何分情况处理
需要计算的情况
1、当前服务返回有数据,并且当前页数据条数小于size的情况,视为最后一页
2、当前服务返回有数据,并且等于size的情况,视为可能还有下一页
3、当前服务返回没有数据,实际表格有数据存在,说明上一页就是最后一页,并且数据的条数刚好等于size(与2一样)
不需要计算的情况
触发中间页码,不需要计算,直接填充数据
代码
data() {
return {
num: 34,
size: 5,
current: 1,
currentBeifen: 1, //备份页码 解决动态数据无法选中新页码问题
total: 0,
estimateStepSize: 5, //预测下一页增加的条数的梯度,有数据扽情况与size保持一致
maxPage: 1, // 已经渲染的动态的最大页码
tableData: [],
tableList: [] //模拟后端返回的未知的总数据
};
},
methods: {
initParams() {
this.total = 0;
this.currentBeifen = 1;
this.maxPage = 1;
this.estimateStepSize = this.size;
},
handleSizeChange(val) {
this.size = val;
this.initParams();
// handleSizeChange和handleCurrentChange同时被触发时,保证handleSizeChange后执行,确定数据能被计算
setTimeout(() => {
this.queryData();
}, 0);
},
handleCurrentChange(val, isCalculate = false) {
this.currentBeifen = val;
this.queryData(isCalculate);
},
/**
* isCalculate: 是否计算动态分页数据
*/
async queryData(isCalculate = true) {
const data = await this.requestTableData(this.currentBeifen, this.size);
const dataLen = data.length;
if (isCalculate) {
// 避免最后一页数据的条数刚好等于size的情况,会继续请求下一页数据,这种情况下一页数据为空,返回到有效的最后一页
if (this.total > 0 && !dataLen) {
this.$message.warning("当前已是最后一页数据!");
this.estimateStepSize = 0;
this.handleCurrentChange(this.currentBeifen - 1, true);
} else {
// 解决:table下一页没有数据,页面闪现问题
this.tableData = [...data];
}
if (dataLen && dataLen < this.size) {
this.total += this.estimateStepSize;
this.estimateStepSize = 0;
}
// 请求当页有数据 并且 当前操作页大于上一页时,总条数加一个size
if (dataLen && dataLen >= this.size) {
this.total += this.estimateStepSize;
}
} else {
// 不计算的时候,直接赋值到当前页
this.tableData = data;
}
this.current = this.currentBeifen; // 数据都渲染了,才能选中成功
this.maxPage = //用于点击下一页或者触发当前页时,是否需要重新计算分页
this.maxPage - this.currentBeifen > 0
? this.maxPage
: this.currentBeifen;
},
showMore() {
// 当前页不是已经渲染的最后一页,点击下一页,不用动态计算
const isCalculate = this.current == this.maxPage;
this.handleCurrentChange(this.current + 1, isCalculate);
}
}
备注:
1、需要注意的是,代码里面用了一个中间变量currentBeifen ,是因为,直接赋值,选中无效,因为数据还未渲染,待数据渲染好了,再赋值
例如:现在只有3页数据,你需要选中第四页,肯定是选不上的,需要等第四页的数据绑上去了,再选,就ok了
2、切换size的时候同时触发了sizeChange和currentChange,此时加了一个定时器,保证最后执行sizeChange,为什么呢?
因为根据目前的设计逻辑,如果这两个位置换了,会出现一种情况,切换了size后,页码丢了一页,就是因为最后执行的currentChange,没有计算数据,对应不上,每次都最后执行sizeChange就没问题
具体现象就是:点击切换size,数据展示没问题,点击下一页,数据还是第一页,后面就正常了,整体下来就是少一页,total没跟上
handleSizeChange(val) {
this.size = val;
this.initParams();
// handleSizeChange和handleCurrentChange同时被触发时,保证handleSizeChange后执行,确定数据能被计算
setTimeout(() => {
this.queryData();
}, 0);
},
3、还有一个注意的点是,table下一页没数据,解决页面闪现的问题
如果先赋值的话,会出现一个情况,先闪现一下空白页,再回到正常的最后一页,所以加了一个处理如下
// 避免最后一页数据的条数刚好等于size的情况,会继续请求下一页数据,这种情况下一页数据为空,返回到有效的最后一页
if (this.total > 0 && !dataLen) {
this.$message.warning("当前已是最后一页数据!");
this.estimateStepSize = 0;
this.handleCurrentChange(this.currentBeifen - 1, true);
} else {
// 解决:table下一页没有数据,页面闪现问题
this.tableData = [...data];
}
效果展示
数据都被动态展示完了,再继续点击下一页,直接给出具体提示
切换size,默认重新开始计算
点击中间页码,数据未重新计算
总结:其他组件也可同理运用,欢迎大家指教