当数据量特别大时,一次性全部展示出所有数据,会造成页面渲染慢,白屏,卡顿的现象。严重影响体验。为解决这样的问题,可以尝试使用虚拟加载dom节点的方式。
原理:根据屏幕高度和一条数据展示所需要的dom节点高度,求出屏幕能展示的数据条数。在渲染数据的div的同级加一个div(高度为所有数据展示完的高度),目的是显示滚动条。监听滚动,根据滚动条位置,计算截取所有数据中能在屏幕中展示的数据段,进行渲染。
代码demo:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script src="https://unpkg.com/vue/dist/vue.global.js"></script><title>虚拟列表</title><style>.v-scroll {height: 600px;width: 400px;border: 3px solid #000;overflow: auto;position: relative;-webkit-overflow-scrolling: touch;}.infinite-list {position: absolute;left: 0;top: 0;right: 0;z-index: -1;}.scroll-list {left: 0;right: 0;top: 0;position: absolute;text-align: center;}.scroll-item {padding: 10px;color: #555;box-sizing: border-box;border-bottom: 1px solid #999;}</style>
</head><body><div id="app"><!--.v-scroll盒子高度固定,目的:出现滚动条--><div ref="list" class="v-scroll" @scroll="scrollEvent($event)"><!--.infinite-list绝对定位高度为所有数据渲染需要的高度,目的:出现滚动条--><div class="infinite-list" :style="{ height: listHeight + 'px' }"></div><!--要渲染的真实数据的dom--><div class="scroll-list" :style="{ transform: getTransform }"><div ref="items" class="scroll-item" v-for="item in visibleData" :key="item.id":style="{ height: itemHeight + 'px',lineHeight: itemHeight + 'px' }">{{ item.msg }}</div></div></div></div><script>let listData = []for (let i = 1; i <= 10000000; i++) {listData.push({id: i,msg: i + ':真实渲染节点'+i})}const { createApp } = VuecreateApp({data() {return {listData: listData,itemHeight: 60,//可视区域高度screenHeight: 600,//偏移量startOffset: 0,//起始索引start: 0,//结束索引end: null,};},computed: {//列表总高度listHeight() {return this.listData.length * this.itemHeight;},//可显示的列表项数visibleCount() {return Math.ceil(this.screenHeight / this.itemHeight)},//偏移量对应的stylegetTransform() {return `translate3d(0,${this.startOffset}px,0)`;},//获取真实显示列表数据(从全部数据中截取)visibleData() {return this.listData.slice(this.start, Math.min(this.end, this.listData.length));}},mounted() {this.start = 0;this.end = this.start + this.visibleCount;},methods: {scrollEvent() {//当前滚动位置let scrollTop = this.$refs.list.scrollTop;//此时的开始索引this.start = Math.floor(scrollTop / this.itemHeight);//此时的结束索引this.end = this.start + this.visibleCount;//此时的偏移量this.startOffset = scrollTop - (scrollTop % this.itemHeight);}}}).mount('#app')</script>
</body></html>
优化:可以配合节流函数进行优化,防止暴力滚动。
var throttle = (func, delay) => { //节流var prev = Date.now();return function () {var context = this;var args = arguments;var now = Date.now();if (now - prev >= delay) {func.apply(context, args);prev = Date.now();}}}