最近工做比较忙很久没写文章了,有一丢丢不知道如何写起了,那就先说说我是为何要开发本文的组件把。公司有一个定位系统,基本上来讲一个定位单位一分钟或者更短就会有一个定位点,这样一天下来就会有不少定位点了,若是表格想要一会儿放一天甚至三天的数据,那么数据量将会特别大(可能会到达5万条左右的数据),若是咱们显示的列又比较多的话,那么表格的卡顿问题就会很明显了。咱们公司web端选择的ui框架是iview ,说实话iview的其余组件还行,不过表格的话在大量数据面前显得很疲软,反而我之前使用的easyui之类的老框架的表格性能和功能上都很好,毕竟它们已经经历了不少优化,表格这个组件的拓展性很大,想要在性能和功能上都作好十分的困难。javascript
easyui是个与时俱进的框架,有一次我点开它的官网发现它已经出了基于如今热门的vue、react、angular的ui组件。因而我此次选择去看看它基于vue的表格,因而我看到了这个组件附上链接www.jeasyui.net/demo_vue/68…。我发现它经过分页延迟加载的方法解决了大数据量卡断的问题,这是我基本可以理解的,不过看完以后我有一些疑问,首先若是他只渲染了一部分数据,在滚动条滚动的时候再加载数据,那么为何滚动条为何一直是那么长。机智的我打开了开发者模式查看了表格部分的html代码html
一看我明白了,图中的表格底部和表格顶部部分就是滚动条高度一直不变的缘由,而中间部分根据滚动条的滚动始终只加载40条数据,这样大数据量的表格卡顿问题就解决了前端
那么思路咱们基本上能够有了,咱们来理一下。vue
若是皮皮怪们将滚动条滚到了大于原本待加载20条数据高度的位置,咱们就用新的处理方式删除全部的40条数据,根据滚动的位置计算当前位置上下各20条的数据。再这个过程中可能会出现表格变白一下的过程,不过我以为应该能够经过遮罩层来处理。java
基本上的思路有了,那么咱们开始实现它吧 (●'◡'●)。react
表格经过2个table标签组成,第一个是表头第二个是数据内容,方便后期拓展。这里偷懒没有把表头和内部内容和tr再单独成一个组件让代码可读性更好以后还能够再优化。git
咱们直接说最主要的逻辑部分,首先咱们看看props和data部分github
props: {
loadNum: {
//默认加载行数
type: [Number, String],
default() {
return 20;
}
},
tdHeight: {
//表格行高
type: [Number, String],
default() {
return 40;
}
},
tableHeight: {
//表格高度
type: [Number, String],
default() {
return "200";
}
},
tableList: {
//全部表格数据
type: Array,
default() {
return [];
}
},
columns: {
//全部表格匹配规则
type: Array,
default() {
return [];
}
},
showHeader: {
type: Boolean,
default: true
}
},
data() {
return {
isScroll: 17,
showLoad: false,
columnsBottom: [], //实际渲染表格规则
showTableList: [], //实际显示的表格数据
loadedNum: 0, //实际渲染的数据数量
dataTotal: 0, //总数据条数
dataTop: 0, //渲染数据顶部的高度
scrollTop: 0, //滚动上下的距离
interval: null, //判断滚动是否中止的定时器
scrollHeight: 0, //数据滚动的高度
selectTr: -1 //选择的行
};
},复制代码
而后咱们看看滚动事件应该作一些什么先上代码web
//滚动条滚动 handleScroll(event) { let bottomScroll = document.getElementById("bottomDiv"); let topScroll = document.getElementById("topDiv"); if (bottomScroll.scrollTop > this.scrollTop) { //记录上一次向下滚动的位置 this.scrollTop = bottomScroll.scrollTop; //向下滚动 this.handleScrollBottom(); } else if (bottomScroll.scrollTop < this.scrollTop) { //记录上一次向上滚动的位置 this.scrollTop = bottomScroll.scrollTop; //向上滚动 this.handleScrollTop(); } else { //左右滚动 this.handleScrollLeft(topScroll, bottomScroll); } }复制代码
首先咱们经过scrollTop这个变量在每次进入滚动事件的时候记录垂直滚动条的位置,若是这个值不变那么此次滚动就是左右滚动,若是这个值变大看那么就是向下滚动,若是这个值变小了那么就是向上滚动。左右滚动的时候咱们须要作的事情就是让表头随着内容一块儿移动,这样就能够达到左右移动表头动上下移动表头固定的效果。框架
//滚动条左右滚动
handleScrollLeft(topScroll, bottomScroll) {
//顶部表头跟随底部滚动
topScroll.scrollTo(bottomScroll.scrollLeft, topScroll.pageYOffset);
},复制代码
若是是向上移动咱们就要作咱们在思路中提升的事情了先看代码
//滚动条向上滚动
handleScrollTop() {
//若是加载的数据小于默认加载的数据量
if (this.dataTotal > this.loadNum) {
let computeHeight = this.dataTop; //数据须要处理的时候的高度
if (
this.scrollTop < computeHeight &&
this.scrollTop >= computeHeight - this.loadNum * this.tdHeight
) {
this.showLoad = true;
//若是滚动高度到达数据显示顶部高度
if (this.dataTotal > this.loadedNum) {
//若是数据总数大于已经渲染的数据
if (this.dataTotal - this.loadedNum >= this.loadNum) {
//若是数据总数减去已经渲染的数据大于等于loadNum
this.dataProcessing(
this.loadNum,
this.loadedNum - this.loadNum,
"top"
);
} else {
this.dataProcessing(
this.dataTotal - this.loadedNum,
this.dataTotal - this.loadedNum,
"top"
);
}
}
} else if (
this.scrollTop <
computeHeight - this.loadNum * this.tdHeight
) {
this.showLoad = true;
let scrollNum = parseInt(this.scrollTop / this.tdHeight); //滚动的位置在第几条数据
if (scrollNum - this.loadNum >= 0) {
this.dataProcessing(this.loadNum * 2, scrollNum, "topAll");
} else {
this.dataProcessing(scrollNum + this.loadNum, scrollNum, "topAll");
}
}
}
},复制代码
//滚动条向下滚动
handleScrollBottom() {
let computeHeight =
this.dataTop +
this.loadedNum * this.tdHeight -
(this.tableHeight - this.tdHeight - 3); //数据须要处理的时候的高度
if (
this.scrollTop > computeHeight &&
this.scrollTop <= computeHeight + this.tdHeight * this.loadNum
) {
this.showLoad = true;
//若是滚动高度到达数据显示底部高度
if (this.dataTotal > this.loadedNum) {
//若是数据总数大于已经渲染的数据
if (this.dataTotal - this.loadedNum >= this.loadNum) {
//若是数据总数减去已经渲染的数据大于等于20
this.dataProcessing(
this.loadedNum - this.loadNum,
this.loadNum,
"bottom"
);
} else {
this.dataProcessing(
this.dataTotal - this.loadedNum,
this.dataTotal - this.loadedNum,
"bottom"
);
}
}
} else if (
this.scrollTop >
computeHeight + this.tdHeight * this.loadNum
) {
this.showLoad = true;
let scrollNum = parseInt(this.scrollTop / this.tdHeight); //滚动的位置在第几条数据
if (scrollNum + this.loadNum <= this.dataTotal) {
this.dataProcessing(scrollNum, this.loadNum * 2, "bottomAll");
} else {
this.dataProcessing(
scrollNum,
this.dataTotal - scrollNum + this.loadNum,
"bottomAll"
);
}
}
},复制代码
计算了好了有4种状况,而且计算出了对应须要删除和新增的数据量。咱们来看看dataProcessing这个函数作了什么事情。
//上下滚动时数据处理
dataProcessing(topNum, bottomNum, type) {
let topPosition = parseInt(this.dataTop / this.tdHeight);
if (type === "top") {
this.showTableList.splice(this.loadedNum - bottomNum, bottomNum); //减去底部数据
for (var i = 1; i <= topNum; i++) {
//加上顶部数据
let indexNum = topPosition - i;
this.tableList[indexNum].index = indexNum + 1;
this.showTableList.unshift(this.tableList[indexNum]);
}
this.loadedNum = this.loadedNum + topNum - bottomNum; //从新计算实际渲染数据条数
this.dataTop = this.dataTop - topNum * this.tdHeight; //从新计算渲染数据的高度
document.getElementById("bottomDiv").scrollTop =
document.getElementById("bottomDiv").scrollTop +
bottomNum * this.tdHeight;
this.scrollTop = document.getElementById("bottomDiv").scrollTop;
} else if (type == "bottom") {
this.showTableList.splice(0, topNum); //减去顶部数据
for (var i = 0; i < bottomNum; i++) {
//加上底部数据
let indexNum = topPosition + this.loadedNum + i;
this.tableList[indexNum].index = indexNum + 1;
this.showTableList.push(this.tableList[indexNum]);
}
this.loadedNum = this.loadedNum - topNum + bottomNum; //从新计算实际渲染数据条数
this.dataTop = this.dataTop + topNum * this.tdHeight; //从新计算渲染数据的高度
document.getElementById("bottomDiv").scrollTop =
document.getElementById("bottomDiv").scrollTop -
topNum * this.tdHeight;
this.scrollTop = document.getElementById("bottomDiv").scrollTop;
} else if (type == "bottomAll") {
this.showTableList = []; //减去顶部数据
let scrollNum = topNum;
for (var i = 0; i < bottomNum; i++) {
//加上底部数据
let indexNum = scrollNum - this.loadNum + i;
this.tableList[indexNum].index = indexNum + 1;
this.showTableList.push(this.tableList[indexNum]);
}
this.loadedNum = bottomNum; //从新计算实际渲染数据条数
this.dataTop = (scrollNum - this.loadNum) * this.tdHeight; //从新计算渲染数据的高度
this.scrollTop = document.getElementById("bottomDiv").scrollTop;
} else if (type == "topAll") {
this.showTableList = []; //减去顶部数据
let scrollNum = bottomNum;
for (var i = 0; i < topNum; i++) {
//加上底部数据
let indexNum = scrollNum - topNum + this.loadNum + i;
this.tableList[indexNum].index = indexNum + 1;
this.showTableList.push(this.tableList[indexNum]);
}
this.loadedNum = topNum; //从新计算实际渲染数据条数
this.dataTop = (scrollNum - topNum + this.loadNum) * this.tdHeight; //从新计算渲染数据的高度
this.scrollTop = document.getElementById("bottomDiv").scrollTop;
}
this.showLoad = false;
},复制代码
最后咱们来讲说以前考虑的top和bottom,一开始咱们就想好了应该用计算属性去作,事实也说明的确这样,咱们看看代码
computed: {
tableOtherTop() {
//表格剩余数据顶部高度
return this.dataTop;
},
tableOtherBottom() {
//表格剩余数据底部高度
return (
this.dataTotal * this.tdHeight -
this.dataTop -
this.loadedNum * this.tdHeight
);
}
},复制代码
这样就能保证top和bottom高度的变化可以触发表格的变化。
top的高度应该就是显示数据顶部的高度(dataTop)。
bottom的高度应该就是数据的总高度-显示的数据的高度(this.loadedNum * this.tdHeight)-top的高度。
最后咱们来看看效果图
这个组件的开发最麻烦的地方就是理清楚各类状况,而后写好各类计算保证不出错。开发的过程当中我也有一种想要本身开发个简易table组件的想法,无奈感受我的水平有限,不过我也在不少地方作了伏笔,等之后有时间再来拓展这个组件,加油~~~///(^v^)\\\~~~。
这里附上个人github地址github.com/github30789…,我把项目已经上传上去了,若是喜欢能够给我个start,谢谢(●'◡'●),可能其中还存在不少问题,也但愿可以获得各位大佬的指点。