问题
一次性渲染过多的数据,会导致页面卡顿。
可以根据以下几个参数,截取显示的数据。
原理
当前滚动窗口高度(viewportHeight),滚动出的距离(scrollTop),每个item的高度(itemHeight),显示窗口之外缓存item个数(buffer),总数据长度(totalLen)
计算截取数据的开始start
Math.max(
Math.floor(scrollTop/itemHeight)-buffer,
0
)
计算结束位置 end
Math.min(
Math.ceil((scrollTop + viewportHeight) / itemHeight + buffer),
totalLen
)
为了使滚动条显示正常,需要模拟高度,给滚动区域加padding进行模拟,计算方式如下
`${end * itemHeight}px 0 ${Math.max(
(totalLen - end) * itemHeight,
0
)}px 0`
具体实现
虚拟列表组件(UInfiniteList)
<template>
<div @scroll="onScroll" style="height: 300px;overflow-y: auto;">
<div class="x-infinite" ref="container" :style="{padding: padding}">
<slot :sliceItems="sliceItems"></slot>
</div>
</div>
</template>
<script>
import { throttle } from "../util/throttle";
export default {
props: {
items: {
required: true
},
itemHeight: {
required: true,
type: Number
}
},
data() {
return {
buffer: 5,
scrollTop: 0,
viewportHeight: 300
};
},
computed: {
start() {
return Math.max(
Math.floor(this.scrollTop / this.itemHeight) - this.buffer,
0
);
},
end() {
return Math.min(
Math.ceil(
(this.scrollTop + this.viewportHeight) / this.itemHeight + this.buffer
),
this.items.length
);
},
sliceItems() {
return this.items.slice(this.start, this.end);
},
padding() {
return `${this.start * this.itemHeight}px 0 ${Math.max(
(this.items.length - this.end) * this.itemHeight,
0
)}px 0`;
}
},
methods: {
onScroll: throttle(function(e) {
this.scrollTop = e.target.scrollTop;
})
}
};
</script>
使用
<template>
<div>
<u-infinite-list :items="items" :item-height="80" #default="{ sliceItems }">
<div v-for="item in sliceItems" :key="item.id" style="height: 60px; border-bottom: 1px solid #ccc">
{{ item.title }}
</div>
</u-infinite-list>
</div>
</template>
<script>
import UInfiniteList from "../../../components/UInfiniteList.vue";
export default {
name: "",
components: {
UInfiniteList
},
data() {
return {
items: []
}
},
created() {
this.items = Array(100).fill(1).map((item, i) => {
return {
title: 'key'+i,
id: i
}
})
},
};
</script>
欢迎批评指正~