于常见的触底懒加载的优劣对比
优势:
- 滚动平滑性: 虚拟列表可以在滚动时实时渲染可见区域内的数据,保持滚动的平滑性,不会因为数据量大而导致滚动卡顿。
- 性能优化: 虚拟列表能够有效减少渲染的 DOM 元素数量,从而提高页面的性能和响应速度。
- 快速渲染: 虚拟列表根据滚动位置计算需要渲染的数据,渲染速度较快,可以更快地展示数据。
劣势:
- 不适用动态数据: 虚拟列表适用于静态数据或数据变化不频繁的情况,对于动态变化的数据需要额外的处理。
效果展示
创建一个父级div
主要添加下面两个属性,其他样式随便
overflow: auto;
position: relative;
然后添加子级div仅用于撑开父级,高度是根据数据量来的,我这里模拟1w条数据 高度55w px
然后再添加 存放数据的盒子 使用绝对定位
数据展示
实现逻辑
滚动实际是撑开的div实现的,与数据展示没有关系,展示数据的box盒子 使用绝对定位和top 让它一直固定在能看到的位置
数据的展示:
上图中可以看到,第一跳数据已经滚动到了顶部以外,当它超出一个行高的时候 它就会被删除替换成地二条数据 比如 开始是 0--9 当滑动的时候 他就变成了 1 -- 10 然后top下移一个行高。
在下边我们看到除了能看到的22 在dom里面还有24 这个就是防止下滑太快导致出现空白区域的
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.content{
width: 500px;
height: 550px;
border: #2d9f75 1px solid;
overflow: auto;
margin: 0 auto;
position: relative;
}
.item-box{
position: absolute;
width: 100%;
top: 0; /* Add top: 0; */
left: 0; /* Add left: 0; */
}
.item-li{
width: 100%;
text-align: center;
height: 55px;
line-height: 55px;
border: antiquewhite 0.5px solid;
}
</style>
</head>
<body>
<div id="app">
<div class="content" ref="contentRef" @scroll="handleScroll">
<!-- 根据总条数和 每行的高度 计算出 高度,次div 只用于撑开父级 出现滚动条-->
<div :style="{'height':`${total * this.lineHeight}px`}"></div>
<!-- 用于绝对定位,展示可视窗口数据-->
<div class="item-box">
<div v-for="(item,index) in visibleList" :key="index" class="item-li">{{item.name}}</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
total:10000,// 总条数 ,也可根据list数组长度计算
lineHeight:55,// 每一行高度
bufferSize:2,// 缓冲数
list:[],// 原数组
visibleList: [],// for循环 展示的数组
},
methods:{
updateVisibleList() {
// 获取父级div dom计算距离顶部距离
const content = this.$refs.contentRef;
// 获取浮动div 设置top值
const itemBox = this.$el.querySelector('.item-box');
// content.scrollTop 是当前滚动条距离顶部的距离 / this.lineHeight(每行高度) 得出可视窗口内 展示的数据开始下标
const startIndex = Math.floor(content.scrollTop / this.lineHeight);
/* content.clientHeight 可视窗口区域高度用于计算 窗口内展示多少行数据 ,
bufferSize缓冲数 在原数据上多显示两行 防止快速滑动的时候出现白色区域*/
const endIndex = startIndex + Math.ceil(content.clientHeight / this.lineHeight) + this.bufferSize;
// list 是1万条数据,visibleList才是正常for循环的数据,需要根据下标赋值
this.visibleList = this.list.slice(startIndex, endIndex);
// 设置绝对定位top 使得 itemBox 始终在我们的视线内
itemBox.style.top = `${startIndex * this.lineHeight}px`;
},
handleScroll(){
this.updateVisibleList();
}
},
mounted(){
// 构造1万条数据 一般来说是从后端请求的数据
for (let i = 0; i < this.total;i++) {
this.list.push({
name:'name'+i
})
}
this.updateVisibleList();
}
})
</script>
</body>
</html>