项目场景:
当前页显示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}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!
如果有不懂可以留言,我看到了应该会回复
如有错误,请多多指教


















