在前端开发中,当需要展示大量数据时,如何保持页面的流畅性是一个挑战。传统的滚动方式会将所有数据一次性渲染到页面,这可能导致页面加载缓慢甚至崩溃。而虚拟滚动技术能够解决这个问题,它只渲染可视区域内的数据,从而提升页面性能。本文将详细解析一个基于Vue框架实现的虚拟滚动示例代码,让我们一步步来看看其中的奥秘。
图例:
首先,让我们看一下HTML结构和基本的样式设置。这段代码主要定义了用于展示纵向和横向滚动列表的两个容器,以及它们的样式设置。
<div id="app">
<div>纵向滚动数据:{{ clist }}div>
<div class="box" :style="`height:${viewH}px;overflow-y:scroll;`" @scroll="handleScroll">
<ul>
<li :style="`transform:translateY(${offsetY}px); height:${itemH}px;`" v-for='i in clist'
:key="i">{{i}}
li>
ul>
div>
<div>横向滚动数据:{{ clist2 }}div>
<div class="box2" :style="`width:${viewW}px;overflow-x:scroll;`" @scroll="handleScroll2">
<div style="display: flex;">
<div :style="`transform:translateX(${offsetX}px); width:${itemW}px;flex-shrink: 0;`"
v-for='i in clist2' :key="i">{{i}}
div>
div>
div>
div>
在样式部分,通过标签设置了一些基本样式,将所有元素的内外边距、列表样式、盒模型等进行了调整,以便后续的布局和展示。
接下来,让我们来看一下数据的初始化和Vue实例的创建。这部分代码主要进行了数据初始化,包括一个包含了一万条数据的数组,并通过Vue实例将数据绑定到页面。
let list = []
for (let index = 0; index < 10000; index++) {
list.push(index)
}
// 创建一个包含10000个元素的数组
new Vue({
el: '#app',
data() {
return {
list, // 上万条总数据
clist: [], // 页面展示的纵向滚动数据
clist2: [], // 页面展示的横向滚动数据
viewH: 500, // 外部纵向滚动容器的高度
itemH: 60, // 单项的高度
scrollH: '', // 整个滚动列表的高度
showNum: '', // 可视区内显示的纵向列表项数量
showNum2: '', // 可视区内显示的横向列表项数量
offsetY: 0, // 纵向滚动时的偏移量
offsetX: 0, // 横向滚动时的偏移量
viewW: 300, // 外部横向滚动容器的宽度
itemW: 60, // 横向列表项的宽度
};
},
})
在Vue实例中,通过data
选项定义了一系列数据,包括总数据列表、展示的纵向和横向滚动数据、外部容器的高度和宽度、单项的高度和宽度等。这些数据将会在后续的滚动事件中使用。
现在,我们来看一下如何实现纵向虚拟滚动列表。在这一部分,代码通过监听滚动事件,动态计算偏移量和显示的数据范围,从而实现了虚拟滚动的效果。
handleScroll(e) {
// 纵向滚动事件处理函数
if (new Date().getTime() - this.lastTime > 10) {
let scrollTop = e.target.scrollTop; // 获取滚动的高度
this.offsetY = scrollTop - (scrollTop % this.itemH);
// 计算偏移量,用于虚拟滚动的位置调整
this.clist = this.list.slice(
Math.floor(scrollTop / this.itemH),
Math.floor(scrollTop / this.itemH) + this.showNum
);
// 根据滚动位置更新显示的列表项数据
this.lastTime = new Date().getTime();
}
},
在handleScroll
方法中,通过获取滚动的高度来计算偏移量,以及通过计算偏移量来确定显示的数据范围。这样,在滚动时只渲染可视区域内的数据,大大提升了性能。注意,代码中加入了时间间隔判断,以确保滚动频率不会过高。
类似地,让我们来看一下如何实现横向虚拟滚动列表。这部分代码与纵向滚动的实现类似,只是方向和数据计算稍有不同。
handleScroll2(e) {
// 横向滚动事件处理函数
if (new Date().getTime() - this.lastTime2 > 10) {
let scrollWidth = e.target.scrollLeft; // 获取滚动的宽度
this.offsetX = scrollWidth - (scrollWidth % this.itemW);
// 计算偏移量,用于虚拟滚动的位置调整
this.clist2 = this.list.slice(
Math.floor(scrollWidth / this.itemW),
Math.floor(scrollWidth / this.itemW) + this.showNum2
);
// 根据滚动位置更新显示的列表项数据
this.lastTime2 = new Date().getTime();
}
},
在handleScroll2
方法中,通过获取滚动的宽度来计算横向偏移量,然后再根据偏移量计算显示的数据范围。这里同样也加入了时间间隔判断,以保证滚动的流畅性。
通过以上的代码分析,我们深入了解了基于Vue实现的高性能虚拟滚动技术。这项技术通过动态计算偏移量和显示的数据范围,使得页面在渲染大量数据时能够保持流畅性。特别是在滚动事件中加入时间间隔判断,进一步优化了滚动性能。
虽然本文只是简单介绍了虚拟滚动的实现思路,但它背后的原理和技术值得我们深入学习和探讨。通过这个示例,我们不仅可以提升页面性能,还可以更好地理解前端渲染机制和优化策略。
希望本文对你理解和掌握虚拟滚动技术有所帮助!如果你有任何疑问或者想进一步深入讨论,欢迎在评论区留言。感谢阅读!
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 省略了一些meta和title标签 -->
<style>
/* 基本样式设置 */
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.13/vue.min.js"></script>
<!-- 引入Vue库 -->
</head>
<body>
<div id="app">
<div>纵向滚动数据:{{ clist }}</div>
<!-- 显示纵向滚动的数据列表 -->
<div class="box" :style="`height:${viewH}px;overflow-y:scroll;`" @scroll="handleScroll">
<!-- 设置可滚动的外部容器,并监听滚动事件 -->
<ul>
<li :style="`transform:translateY(${offsetY}px); height:${itemH}px;`" v-for='i in clist'
:key="i">{{i}}
</li>
<!-- 使用虚拟滚动技术,只渲染可视区域内的列表项 -->
</ul>
</div>
<div>横向滚动数据:{{ clist2 }}</div>
<!-- 显示横向滚动的数据列表 -->
<div class="box2" :style="`width:${viewW}px;overflow-x:scroll;`" @scroll="handleScroll2">
<!-- 设置可滚动的外部容器,并监听滚动事件 -->
<div style="display: flex;">
<!-- 使用flex布局,用于横向排列列表项 -->
<div :style="`transform:translateX(${offsetX}px); width:${itemW}px;flex-shrink: 0;`"
v-for='i in clist2' :key="i">{{i}}
</div>
<!-- 使用虚拟滚动技术,只渲染可视区域内的列表项 -->
</div>
</div>
</div>
<script>
let list = []
for (let index = 0; index < 10000; index++) {
list.push(index)
}
// 创建一个包含10000个元素的数组
new Vue({
el: '#app',
data() {
return {
list, // 上万条总数据
clist: [], // 页面展示的纵向滚动数据
clist2: [], // 页面展示的横向滚动数据
viewH: 500, // 外部纵向滚动容器的高度
itemH: 60, // 单项的高度
scrollH: '', // 整个滚动列表的高度
showNum: '', // 可视区内显示的纵向列表项数量
showNum2: '', // 可视区内显示的横向列表项数量
offsetY: 0, // 纵向滚动时的偏移量
offsetX: 0, // 横向滚动时的偏移量
viewW: 300, // 外部横向滚动容器的宽度
itemW: 60, // 横向列表项的宽度
};
},
mounted() {
// 初始化操作在挂载后执行
this.scrollH = this.list.length * this.itemH;
// 计算整个滚动列表的高度
this.showNum = Math.floor(this.viewH / this.itemH) + 4;
// 计算可视区内能够容纳的纵向列表项数量
this.clist = this.list.slice(0, this.showNum);
// 初始化显示的纵向列表项数据
this.lastTime = new Date().getTime();
this.scrollW = this.list.length * this.itemW;
// 计算整个滚动列表的宽度
this.showNum2 = Math.floor(this.viewW / this.itemW) + 4;
// 计算可视区内能够容纳的横向列表项数量
this.clist2 = this.list.slice(0, this.showNum2);
// 初始化显示的横向列表项数据
this.lastTime2 = new Date().getTime();
},
methods: {
handleScroll(e) {
// 纵向滚动事件处理函数
if (new Date().getTime() - this.lastTime > 10) {
let scrollTop = e.target.scrollTop; // 获取滚动的高度
this.offsetY = scrollTop - (scrollTop % this.itemH);
// 计算偏移量,用于虚拟滚动的位置调整
this.clist = this.list.slice(
Math.floor(scrollTop / this.itemH),
Math.floor(scrollTop / this.itemH) + this.showNum
);
// 根据滚动位置更新显示的列表项数据
this.lastTime = new Date().getTime();
}
},
handleScroll2(e) {
// 横向滚动事件处理函数
if (new Date().getTime() - this.lastTime2 > 10) {
let scrollWidth = e.target.scrollLeft; // 获取滚动的宽度
this.offsetX = scrollWidth - (scrollWidth % this.itemW);
// 计算偏移量,用于虚拟滚动的位置调整
this.clist2 = this.list.slice(
Math.floor(scrollWidth / this.itemW),
Math.floor(scrollWidth / this.itemW) + this.showNum2
);
// 根据滚动位置更新显示的列表项数据
this.lastTime2 = new Date().getTime();
}
},
},
});
</script>
</body>
</html>