传统的无限滚动数据表格并不会一次性把所有数据都加载并渲染出来,它首先加载一批数据,这批数据要远大于一屏数据的承载量,但也不能太多,避免首屏数据加载效率不佳,具体数量可以由开发者根据实际情况确定。接着通过监控滚动条位置来实时加载更多的数据,比如当滚动条触底时(或即将触底时),开始加载当前已显示的数据集之后的数据,当滚动条触顶时(或即将触顶时),开始加载当前已显示的数据集之前的数据
先创建一个视图模板
<template>
<div class="container">
<div @mousewheel="listWheel" class="list">
<div class="item" :key="item.id" v-for="item of dataArray">
<div class="from">{{ item.userName }}</div>
<div class="message">{{ item.message }}</div>
</div>
</div>
<!--模拟滚动条-->
<div @scroll="containerScroll" class="scroller">
<div class="heightBox"></div>
</div>
</div>
</template>
再来优化样式
<style>
html,
body {
text-align: center;
height: 100%;
margin: 0px;
padding: 0px;
}
::-webkit-scrollbar-track {
background-color: #242d3e;
-webkit-box-shadow: inset 0 0 6px #242d3e;
}
/*定义滚动条高宽及背景*/
::-webkit-scrollbar {
width: 6px;
background-color: #242d3e;
}
/*定义滚动条*/
::-webkit-scrollbar-thumb {
background-color: #09a5ee;
}
#app {
height: 100%;
background: #eee;
}
.container {
width: 50%;
height: 50%;
margin: 0px auto;
position: relative;
top: 50%;
transform: translateY(-50%);
background: #fff;
border: 1px solid #ccc;
border-radius: 3px;
overflow: hidden;
display: flex;
}
.list {
flex: 1;
}
.scroller {
width: 6px;
height: 100%;
overflow-y: auto;
}
.item {
height: 66px;
overflow: hidden;
line-height: 22px;
display: flex;
padding: 8px;
text-align: left;
}
.item .from {
height: 66px;
width: 66px;
line-height: 66px;
font-size: 16px;
color: #666;
font-weight: bold;
}
.item .message {
flex: 1;
font-size: 12px;
}
</style>
编写行为层的逻辑处理
<script lang="ts">
import { nextTick, onMounted, ref } from "vue";
export default {
setup() {
let dbConn;
let rowCount = 0;
let dataArray = ref([]);
let containerDom: HTMLElement;
let scroller: HTMLElement;
let bottomDom: HTMLElement;
let totalHeight;
let lineHeight = 66;
let pageSize;
let initDb = async () => {
let messageTable = {
name: "message",
columns: {
id: { primaryKey: true, autoIncrement: true },
userName: { notNull: true, dataType: "string" },
message: { notNull: true, dataType: "string" },
createTime: { notNull: true, dataType: "number" },
},
};
let db = {
name: "im",
tables: [messageTable],
};
// 真实业务中替换为接口数据
let JsStoreWorker = require("jsstore/dist/jsstore.worker.commonjs2");
window["JsStoreWorker"] = JsStoreWorker;
let JsStore = require("jsstore");
dbConn = new JsStore.Connection();
await dbConn.initDb(db);
};
/**
* 初始化占位符数据
*/
let initData = async () => {
let arr = [
`李清照:天接云涛连晓雾,星河欲转千帆舞。仿佛梦魂归帝所,闻天语,殷勤问我归何处。
我报路长嗟日暮,学诗谩有惊人句。九万里风鹏正举。风休住,蓬舟吹取三山去!`,
`辛弃疾:醉里挑灯看剑,梦回吹角连营。八百里分麾下炙,五十弦翻塞外声,沙场秋点兵。
马作的卢飞快,弓如霹雳弦惊。了却君王天下事,赢得生前身后名。可怜白发生!`,
`范仲淹:塞下秋来风景异,衡阳雁去无留意。四面边声连角起。千嶂里,长烟落日孤城闭。
浊酒一杯家万里,燕然未勒归无计,羌管悠悠霜满地。人不寐,将军白发征夫泪。`,
`岳飞:怒发冲冠,凭阑处、潇潇雨歇。抬望眼,仰天长啸,壮怀激烈。三十功名尘与土,八千里路云和月。莫等闲、白了少年头,空悲切。
靖康耻,犹未雪。臣子恨,何时灭。驾长车,踏破贺兰山缺。壮志饥餐胡虏肉,笑谈渴饮匈奴血。待从头、收拾旧山河,朝天阙。`,
`柳永:伫倚危楼风细细,望极春愁,黯黯生天际。草色烟光残照里,无言谁会凭阑意。
拟把疏狂图一醉,对酒当歌,强乐还无味。衣带渐宽终不悔,为伊消得人憔悴。`,
`晏殊:槛菊愁烟兰泣露,罗幕轻寒,燕子双飞去。明月不谙离恨苦,斜光到晓穿朱户。
昨夜西风凋碧树,独上高楼,望尽天涯路。欲寄彩笺兼尺素,山长水阔知何处。`,
];
for (let i = 0; i <= 999999; i++) {
let strArr = arr[i % 6].split(":");
let obj = {
userName: strArr[0],
message: strArr[1],
createTime: Date.now(),
};
await dbConn.insert({
into: "message",
values: [obj],
});
}
};
let debounce = (fn) => {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.call(this, arguments);
}, 160);
};
};
onMounted(async () => {
await initDb();
// initData();
rowCount = await dbConn.count({ from: "message" });
totalHeight = rowCount * lineHeight;
let heightBox: HTMLElement = document.querySelector(
".scroller .heightBox"
);
heightBox.style.height = `${totalHeight}px`;
containerDom = document.querySelector(".container");
scroller = document.querySelector(".scroller");
// bottomDom = containerDom.querySelector(".bottom");
let containerHeight = containerDom.clientHeight;
pageSize = Math.ceil(containerHeight / lineHeight); //向上取整,要多取
// topDom.style.height = "0px";
// bottomDom.style.height = `${totalHeight - (pageSize + 6) * lineHeight}px`;
dataArray.value = await dbConn.select({
from: "message",
limit: pageSize + 6,
order: {
by: "createTime",
type: "asc",
},
});
});
// 模拟滚动条事件监听
let containerScroll = async (e) => {
let topIndex = Math.floor(scroller.scrollTop / lineHeight); //向下取整,也是要多取
dataArray.value = await dbConn.select({
from: "message",
skip: topIndex,
limit: pageSize + 6,
order: {
by: "createTime",
type: "asc",
},
});
let remainHeight = scroller.scrollTop - lineHeight * topIndex;
containerDom.scrollTop = remainHeight;
};
let listWheel = async (e: WheelEvent) => {
let top = scroller.scrollTop + e.deltaY;
if (top < 0) top = 0;
else if (top > totalHeight - containerDom.clientHeight)
top = totalHeight - containerDom.clientHeight;
scroller.scrollTop = top;
};
return {
dataArray,
rowCount,
containerScroll,
listWheel,
};
},
};
</script>