数万条数据优化
当一个页面的列表有大量数据如有 100000 条如何优化渲染?
当我们将 10W 条全部在页面加载的时候渲染出来,可以很明显的感受到加载速度缓慢并且可以看到 DOM 树非常非常的长当快速上下滚动时候,偶尔还会卡顿,下面我们用代码实现下这里用 vue 实现
<template>
<div class="list-view">
<!-- @scroll="handleScroll($event)" -->
<div class="list-view-item" :style="{ height: itemHeight + 'px' }" v-for="item in data" :key="item.value">{{ item }}</div>
</div>
</template>
<style>
.list-view {
height: 600px;
width: 200px;
overflow: auto;
position: relative;
border: 1px solid #666;
}
.list-view-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list-view-content {
position: absolute;
height: 600px;
z-index: 99;
left: 0;
right: 0;
top: 0;
}
.list-view-item {
padding: 5px;
color: #000;
height: 30px;
width: 200px;
line-height: 30px;
box-sizing: border-box;
}
</style>
<script>
export default {
props: {
itemHeight: {
type: Number,
default: 30
}
},
mounted() {},
created() {
for (let i = 0; i < 100000; i++) {
this.data.push('第' + i + '条');
}
},
data() {
return {
data: []
};
},
methods: {}
};
</script>
效果图

可以很明显的看到当快速滑动时画面的卡顿
如何优化?
我们知道屏幕都是有一个可视范围的,我们因此就可以当节点在可视范围时就显示出来,当节点不在可视范围时候就移除,因此我们只要渲染可视范围的节点即可
画个图

主要分以下几个步骤。 1.计算窗口可视范围,并根据可视范围计算出一屏幕可见的节点。 2.监听滚动计算每次滚动完成后的初始节点和末尾节点 3.根据新的起始节点替换可视区域数据
具体实现参考了一篇网上的博客具体链接会在下面注明:
<template>
<div class="list-view" @scroll="handleScroll($event)">
<div class="list-view-phantom" :style="{ height: listheight + 'px' }"></div>
<div ref="content" class="list-view-content">
<div class="list-view-item" :style="{ height: itemHeight + 'px' }" v-for="item in visibleData" :key="item.value">{{ item }}</div>
</div>
</div>
</template>
<script>
export default {
props: {
itemHeight: {
type: Number,
default: 30
}
},
mounted() {
this.visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight); //计算可视区域高度可显示数据条目数
this.start = 0; //起始数据下标
this.end = this.start + this.visibleCount; //末尾最后一条
this.visibleData = this.data.slice(this.start, this.end); // 初始化可视区域数据
},
created() {
for (let i = 0; i < 100000; i++) {
this.data.push('第' + i + '条'); //初始化10W条数据
}
this.listheight = this.data.length * 30; //计算10w条元素的总高度
},
data() {
return {
data: [],
start: 0,
end: null,
visibleCount: null, //
visibleData: [],
scrollTop: 0,
listheight: ''
};
},
methods: {
//滚动监听
handleScroll(event) {
const scrollTop = this.$el.scrollTop; //监听距离顶部高度
const fixedScrollTop = scrollTop - (scrollTop % 30); //修复移动位置不精确问题
this.$refs.content.style.webkitTransform = `translate3d(0, ${fixedScrollTop}px, 0)`; //实时位移
this.start = Math.floor(scrollTop / 30); //重新计算起始条目下标
this.end = this.start + this.visibleCount; // 重新计算末尾小标
this.visibleData = this.data.slice(this.start, this.end); //重新装载可视区域数据
}
}
};
</script>
<style>
.list-view {
height: 400px;
width: 200px;
overflow: auto;
position: relative;
border: 1px solid #666;
}
.list-view-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list-view-content {
position: absolute;
height: 400px;
z-index: 99;
left: 0;
right: 0;
top: 0;
}
.list-view-item {
padding: 5px;
color: #000;
height: 30px;
width: 200px;
line-height: 30px;
box-sizing: border-box;
}
</style>
来看看实现后的快速滚动的效果


这里主要利用了绝对布局,利用两个绝对布局一个没有任何元素,但高度为所有节点高度的合,利用这个撑开父节点使其滚动条出现,第二个节点高度和可视区域高度一致,当不断滚动的时候利用位移调整位置。
画个图

无论如何滑动,可视区域的范围就那么大,因此不会造成页面大量 DOM 节点的存在导致卡顿问题的出现
参考实现https://zhuanlan.zhihu.com/p/26022258