虚拟滚动实现页面百万数据滚动
1525 2023年09月11日 前端1.概念
虚拟滚动是一种优化长列表性能的技术,它通过按需渲染的方式,只渲染可见部分的列表项,而不是渲染整个列表。
2.技术背景
在列表滚动中,如果列表数据有几万,几十万,就会生成大量的DOM元素,这样就会导致页面加载慢、卡顿甚至崩溃。因此,虚拟滚动技术,就是为了解决此问题而产生的。
3.原理
在具体实现上,虚拟滚动技术需要控制大列表中的DOM元素的创建与销毁,只创建可视区域内的DOM元素,非可视区域的DOM元素不创建。这样,在渲染大列表中的数据时,只创建少数的DOM元素,从而提高性能。虚拟滚动通过JS模拟的滚动来减少真实的滚动操作,防止页面加载慢、卡顿等问题,改善用户体验。
4.实现
以下是一个简单的示例,演示如何使用JavaScript和CSS实现虚拟滚动:
原生代码实现:
1)创建一个包含要显示的内容的HTML元素。
<div id="virtual-scroll">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<!-- 更多内容 -->
</div>
2)使用CSS设置容器的高度和溢出属性,以便内容可以滚动。
#virtual-scroll {
height: 500px; /* 容器高度 */
overflow-y: auto; /* 垂直方向滚动 */
}
3)使用JavaScript监听滚动事件,并动态创建和删除可见区域内的内容项。
const container = document.getElementById('virtual-scroll');
const itemHeight = 50; // 每个内容项的高度
const visibleItems = Math.floor(container.clientHeight / itemHeight); // 可见区域内的内容项数量
function updateVisibleItems() {
const scrollTop = container.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = startIndex + visibleItems;
// 删除超出可见区域的内容项
const removeItems = container.getElementsByClassName('item');
for (let i = 0; i < removeItems.length; i++) {
if (i < startIndex || i >= endIndex) {
removeItems[i].remove();
}
}
// 创建新的内容项并添加到容器中
for (let i = startIndex; i < endIndex; i++) {
const item = document.createElement('div');
item.classList.add('item');
item.textContent = `Item ${i + 1}`;
container.appendChild(item);
}
}
// 监听滚动事件
container.addEventListener('scroll', updateVisibleItems);
这个示例中,我们首先计算可见区域内的内容项数量,然后在滚动事件发生时动态创建和删除可见区域内的内容项。这种方法可以通过减少DOM元素的数量来提高性能,因为只需要渲染可见区域内的内容项。
vue中实现
1)创建一个虚拟滚动组件(VirtualScroll.vue):
<template>
<div class="virtual-scroll" ref="scrollContainer" @scroll="handleScroll">
<div class="phantom" :style="{ height: phantomHeight + 'px' }"></div>
<div class="visible-items" :style="{ transform: `translate3d(0, ${offsetTop}px, 0)` }">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "VirtualScroll",
props: {
itemHeight: {
type: Number,
default: 20,
},
itemsCount: {
type: Number,
required: true,
}
},
data() {
return {
visibleItems: 0,
offsetTop: 0,
inThrottle:false, //截流参数
timeout:false, //防抖参数
};
},
computed: {
phantomHeight() {
return this.itemsCount * this.itemHeight;
},
},
methods: {
handleScroll() {
if (!this.inThrottle) {
this.$nextTick(()=>{
this.inThrottle = true;
clearTimeout(this.timeout);
this.timeout = setTimeout(()=>{
const scrollTop = this.$refs.scrollContainer.scrollTop;
const startIndex = Math.floor(scrollTop / this.itemHeight);
const endIndex = startIndex + this.visibleItems;
this.offsetTop = scrollTop;
// 更新可见区域内的内容项
this.$emit("update-items", startIndex, endIndex);
this.inThrottle = false;
},300)
})
}
},
},
mounted() {
this.$nextTick(()=>{
this.visibleItems = Math.floor(this.$refs.scrollContainer.clientHeight / this.itemHeight);
this.$refs.scrollContainer.addEventListener("scroll", this.handleScroll);
})
},
beforeDestroy() {
this.$nextTick(()=>{
this.$refs.scrollContainer.removeEventListener("scroll", this.handleScroll);
})
},
};
</script>
<style scoped>
.virtual-scroll {
position: relative;
width: 100%;
height: 100%;
overflow-y: auto;
}
.phantom {
position: absolute;
top: 0;
left: 0;
right: 0;
}
.visible-items {
position: absolute;
height: 100%;
top: 0;
left: 0;
right: 0;
}
</style>
2) 在父组件中使用虚拟滚动组件:
<template>
<div id="List">
<virtual-scroll :item-height="50" :items-count="items.length" @update-items="updateItems">
<div v-for="index in showItems" :key="index" class="item">{{ index}}</div>
</virtual-scroll>
</div>
</template>
<script>
import VirtualScroll from "@/components/VirtualScroll.vue";
export default {
name: "App",
components: { VirtualScroll }, // 注册虚拟滚动组件
data() {
return {
items: [],
showItems:[],
};
},
mounted() {
this.$nextTick(()=>{
this.getData();
})
},
methods: {
async updateItems(startIndex, endIndex) {
this.showItems = await this.items.slice(startIndex, endIndex)
console.log(startIndex, endIndex)
},
async getData(){
for(let i=0; i<1000000; i++){
this.items.push('item-' + i)
}
this.showItems = this.items.slice(0, 20)
}
},
};
</script>
<style>
#List{
height: 100vh;
}
.item {
height: 50px; /* 内容项的高度 */
color: #2C3E50;
}
</style>
uiapp中实现
1)子组件 VirtualScroll.vue
<template>
<view class="virtual-scroll" ref="scrollContainer" @scroll="handleScroll">
<view class="phantom" :style="{ height: phantomHeight + 'px' }"></view>
<view class="visible-items" :style="{ transform: `translate3d(0, ${offsetTop}px, 0)` }">
<slot></slot>
</view>
</view>
</template>
<script>
export default {
name: "VirtualScroll",
props: {
itemHeight: {
type: Number,
default: 20,
},
itemsCount: {
type: Number,
required: true,
}
},
data() {
return {
visibleItems: 0,
offsetTop: 0,
inThrottle:false, //截流参数
timeout:false, //防抖参数
};
},
computed: {
phantomHeight() {
return this.itemsCount * this.itemHeight;
},
},
methods: {
handleScroll() {
if (!this.inThrottle) {
this.$nextTick(()=>{
this.inThrottle = true;
clearTimeout(this.timeout);
this.timeout = setTimeout(()=>{
const scrollTop = this.$refs.scrollContainer.$el.scrollTop;
const startIndex = Math.floor(scrollTop / this.itemHeight);
const endIndex = startIndex + this.visibleItems;
this.offsetTop = scrollTop;
// 更新可见区域内的内容项
this.$emit("update-items", startIndex, endIndex);
this.inThrottle = false;
},300)
})
}
},
},
mounted() {
this.$nextTick(()=>{
if(this.$refs.scrollContainer){
this.visibleItems = Math.floor(this.$refs.scrollContainer.$el.clientHeight / this.itemHeight);
this.$refs.scrollContainer.$el.addEventListener("scroll", this.handleScroll);
}
})
},
beforeDestroy() {
this.$nextTick(()=>{
if(this.$refs.scrollContainer){
this.$refs.scrollContainer.$el.removeEventListener("scroll", this.handleScroll);
}
})
},
};
</script>
<style scoped>
.virtual-scroll {
position: relative;
width: 100%;
height: 100%;
overflow-y: auto;
}
.phantom {
position: absolute;
top: 0;
left: 0;
right: 0;
}
.visible-items {
position: absolute;
height: 100%;
top: 0;
left: 0;
right: 0;
}
</style>
2)父组件 index.vue
<template>
<view class="content">
<view class="scroll-left">
22
</view>
<view class="scroll-right" >
<virtual-scroll :item-height="50" :items-count="items.length" @update-items="updateItems">
<view v-for="index in showItems" :key="index" class="item">{{ index}}</view>
</virtual-scroll>
</view>
</view>
</template>
<script>
import VirtualScroll from "@/components/VirtualScroll.vue"; // 导入虚拟滚动组件的路径可能有所不同,根据实际情况调整
export default {
name: "App",
components: { VirtualScroll }, // 注册虚拟滚动组件
data() {
return {
items: [],
showItems:[],
};
},
mounted() {
this.$nextTick(()=>{
this.getData();
})
},
methods: {
async updateItems(startIndex, endIndex) {
this.showItems = await this.items.slice(startIndex, endIndex)
console.log(startIndex, endIndex)
},
async getData(){
for(let i=0; i<1000000; i++){
this.items.push('item-' + i)
}
this.showItems = this.items.slice(0, 20)
}
},
};
</script>
<style scoped lang="less">
.content{
// 滚动条不可见
display: flex;
flex-direction: row;
.scroll-left{
&::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
width: 200rpx;
height: 90vh;
overflow-y: auto;
background-color: aliceblue;
}
.scroll-right{
&::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
flex: 1;
height: 90vh;
background-color: aqua;
.item {
height:50px; /* 内容项的高度 */
}
}
}
</style>
-
使用Echarts画甘特图
Echarts是一个非常强大的图表库, 下面我们来使用它来画甘特图,
-
go语言怎么连接mysql,并实现增删改查
要使用Go语言连接MySQL,需要使用第三方库。常用的库包括: 这里以go-sql-driver/mysql为 […]
-
beego实现模块化开发
Beego 框架可以通过模块化开发来提高项目的可维护性和可扩展性,可以将一个大型的应用划分为多个模块,每个模块独立维护,有自己的控制器、视图和模型等。
-
Beego实现JWT
Beego是一个基于Go语言的Web框架,实现JWT认证可以通过beego的中间件机制来实现,下面是一个简单的 […]
-
虚拟滚动实现页面百万数据滚动
1.概念 虚拟滚动是一种优化长列表性能的技术,它通过按需渲染的方式,只渲染可见部分的列表项,而不是渲染整个列表 […]
-
go实现MD5
在这个示例中,我们使用了Go标准库中的crypto/md5包来计算一个字符串的MD5值。首先,我们将字符串转换 […]
-
Linux服务器Rsync结合inotify同步文件
一、实现效果 服务器A:192.168.161.150 (分布服务器)(rsync客户端+inotify) 服务器B:192.168.161.151 (WEB服务器1)(rsync服务端) 服务器C:192.168.161.152 (WEB服务器2)(rsync服务端) 说明:服务器A有文件更新,自动同步到服务器B和C
-
使用Axios+PHP+JWT实现登录认证
JWT(JSON Web Token),顾名思义就是可以在Web上传输的token,这种token是用JSON格式进行format的。它是一个开源标准(RFC7519),定义了一个紧凑的自包含的方式在不同实体之间安全的用JSON格式传输信息。
-
PHP解决雪花算法ID在前端精度丢失的问题
雪花算法(Snowflake)是一种用于生成全局唯一ID的算法,其基本思路是结合时间戳、机器ID、租约ID和序列号生成一个64位的数据,从而保证ID的唯一性。 雪花算法的生成过程如下:
-
JavaScript实现滑动验证码
滑动验证码(也叫做滑动拼图验证码)是一种用户交互形式的验证码,通常用于网页或移动应用的登录、注册或敏感操作。其 […]