项目场景:
当前页显示10w多条数据,不做分页的情况进行渲染。加载和渲染页面会非常慢,滚动也非常卡顿
解决方案:
之渲染可视窗口的列表,其他列表不进行渲染。通过修改偏移量高度进行滚动列表。
vue2版本
virtualList 组件包
注意:
组件必传containerHeight高度(容器高度)和数据列表listData
<template>
<div ref="listRef" :style="{height:containerHeight}" class="listContainer" @scroll="scrollEvent($event)">
<div class="listPhantom" :style="{ height: computedListHeight + 'px' }"></div>
<div class="list" ref="infiniteListRef" :style="{ transform: getTransform }">
<div ref="items"
class="list-item"
v-for="item in visibleData"
:key="item.id"
:style="{height:'100%' }"
><slot :data="item" /></div>
</div>
</div>
</template>
<script>
export default {
name: 'virtualList',
props: {
//所有列表数据
listData:{
type:Array,
default:()=>[]
},
//容器高度
containerHeight:{
type:String,
default:"100%"
}
},
watch:{
//监听列表数据
listData:{
handler() {
this.visibleData = this.listData.slice(this.start, Math.min(this.end,this.listData.length));
},
deep: true,
immediate: true
},
//监听可视化数据,修改每一个列的高度
visibleData:{
handler() {
this.$nextTick(()=>{
this.itemHeight = this.$refs.infiniteListRef.offsetHeight
})
}
}
},
computed:{
//列表总高度
computedListHeight(){
return this.listData.length * this.itemHeight;
},
//可显示的列表项数
computedVisibleCount(){
return Math.ceil(this.screenHeight / this.itemHeight)
},
//偏移量对应的style
getTransform(){
return `translate3d(0,${this.startOffset}px,0)`;
}
},
data() {
return {
//可见数据
visibleData:[],
//每列高度
itemHeight:200,
//可视区域高度
screenHeight:0,
//偏移量
startOffset:0,
//起始索引
start:0,
//结束索引
end:null,
};
},
methods: {
scrollEvent() {
//当前滚动位置
let scrollTop = this.$refs.listRef.scrollTop;
//此时的开始索引
this.start = Math.floor(scrollTop / this.itemHeight);
//此时的结束索引
this.end = this.start + this.computedVisibleCount;
//此时的偏移量
this.startOffset = scrollTop - (scrollTop % this.itemHeight);
},
//页面初始化
handleInit(){
this.screenHeight = this.$el.clientHeight;//客户端高度
this.start = 0;//列表开始索引
this.end = this.start + this.computedVisibleCount;//列表结束索引
}
},
mounted() {
this.handleInit()
},
}
</script>
<style scoped lang="less">
.listContainer {
overflow: auto;
position: relative;
-webkit-overflow-scrolling: touch;
}
.listPhantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list {
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
z-index:10;
}
.list-item {
padding: 10px;
color: #555;
box-sizing: border-box;
}
</style>
应用virtualList
<template>
...
<VirtualList :containerHeight="containerHeight" :listData="rows" >
<template slot-scope="row">
{{row.data}}
</template>
</VirtualList>
...
</template>
<script>
import VirtualList from "./virtualList.vue";
export default{
data(){
return(){
containerHeight:"",//自己设置虚拟滚动列表容器高度
}
},
created() {
this.containerHeight = document.documentElement.offsetHeight+'px'
},
}
</script>
vue3版本
virtualList 组件包
注意:
组件必传data高度(容器高度)和数据列表containerHeight
<template>
<div>
<div class="listContainer" :style="{height:props.containerHeight}" @scroll="handleScroll">
<div class="listContainerPhantom" :style="{ height: listHeight + 'px' }"></div>
<div class="listContainerList" :style="{ transform: computedTransform }">
<template v-for="(item,index) in computedVisibleData">
<slot :data="item"></slot>
</template>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {computed, nextTick, onMounted, ref, watch} from "vue";
let startNum = ref(0)
let endNum = ref(0)
let startOffset = ref(0);//偏移量
let listHeight = ref(0);//列表总高度
let listItemHeight = ref(0);//每项高度
interface propsInterface{
data?:any //列表数据
containerHeight?:string //容器高度
}
let props = withDefaults(defineProps<propsInterface>(),{
//所有数据
data:[],
containerHeight:"",
})
const computedTransform = computed(()=>{
return `translate3d(0,${startOffset.value}px,0)`;
})
//可看得见的数据
const computedVisibleData = computed(()=>{
return props.data.slice(startNum.value,Math.min(endNum.value,props.data.length))
})
const handleScroll = ()=>{
let listContainer = document.querySelector(".listContainer");//列表容器
let listContainerHeight = listContainer.offsetHeight
//可显示的数量
let showNum = Math.ceil(listContainerHeight / listItemHeight.value)
//滚动高度
let contentScrollTop = Math.floor(listContainer.scrollTop);
let curNum = Math.ceil(contentScrollTop / listItemHeight.value);
startNum.value = curNum
endNum.value = showNum + startNum.value
//滚动高度减去空隙
startOffset.value = contentScrollTop - (contentScrollTop%showNum)
}
const handleInit = ()=>{
let listContainer = document.querySelector(".listContainer");//列表容器
let listItem = document.querySelector(".listItem");//每一列
let listContainerHeight = listContainer?.offsetHeight
listItemHeight.value = listItem?.offsetHeight??1
//列表总高度
listHeight.value = props.data.length*listItemHeight.value
startNum.value = 0
let showNum = Math.ceil(listContainerHeight / listItemHeight.value)
endNum.value = startNum.value + showNum
}
//监听列表高度,有变化,重新初始化
watch(()=>listItemHeight.value,(value)=>{
handleInit()
},{immediate:true,deep:true})
onMounted(()=>{
handleInit()
nextTick(()=>{
listItemHeight.value = document.querySelector(".listItem").offsetHeight
})
})
</script>
<style scoped lang="less">
.listContainer{
height:400px;
overflow: auto;
position: relative;
-webkit-overflow-scrolling: touch;
.listContainerPhantom{
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.listContainerList{
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
}
}
</style>
应用virtualList
<template>
...
<VirtualList :data="data" :containerHeight="virtualListHeight" >
<template #default="row">
<div class="listItem u-f u-f-ac u-f-spa" >
{{row.data.id}}
</div>
</template>
</VirtualList>
...
</template>
<script setup lang="ts">
const VirtualList = defineAsyncComponent(()=>import("./virtualList.vue"))
let data = ref([
{
id:1,
},
{
id:2,
},
{
id:3,
},
{
id:4,
},
{
id:5,
},
{
id:6,
},
{
id:7,
},
{
id:8,
},
{
id:9,
},
{
id:10,
},
{
id:11,
},
{
id:12,
},
{
id:13,
},
{
id:14,
},
{
id:15,
}
])
let virtualListHeight = ref("")
onMounted(()=>{
virtualListHeight.value = (document.documentElement.offsetHeight-500)+"px"
})
</script>
最终效果:
✨ 踩坑不易,还希望各位大佬支持一下 \textcolor{gray}{踩坑不易,还希望各位大佬支持一下} 踩坑不易,还希望各位大佬支持一下
📃 个人主页: \textcolor{green}{个人主页:} 个人主页: 沉默小管
📃 个人网站: \textcolor{green}{个人网站:} 个人网站: 沉默小管
📃 个人导航网站: \textcolor{green}{个人导航网站:} 个人导航网站: 沉默小管导航网
📃 我的开源项目: \textcolor{green}{我的开源项目:} 我的开源项目: vueCms.cn
🔥 技术交流 Q Q 群: 837051545 \textcolor{green}{技术交流QQ群:837051545} 技术交流QQ群:837051545
👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!
如果有不懂可以留言,我看到了应该会回复
如有错误,请多多指教