前言
虚拟列表,最近听到这个概念,然后我做了下了解,果然是个好东西,当你面试的时候,如何优化后端返回的大量数据列表,如果你回答,做分页处理,嗯,这个也是不错的回答,然后面试官的你是中级水平,如果你回答,使用虚拟列表,恭喜你,你回答面试官心坎里了。你的级别也就能上一个台阶,当然,不能因为回答,一道面试题,就确定你级别,但是可以给面试官留下很好的印象,说不定你就是优先录取的对象。
定义
虚拟列表:其核心思想就是在处理用户滚动时,只改变列表在可视区域的渲染部分,然后使用padding或者translate来让渲染的列表偏移到可视区域中,给用户平滑滚动的感觉。
虚拟列表出现前的原因:
长列表渲染一直以来都是前端比较头疼的一个问题,如果想要在网页中放大量的列表项,纯渲染的话,对于浏览器性能将会是个极大的挑战,会造成滚动卡顿,整体体验非常不好,主要有以下问题:
- 页面等待时间极长,白屏时间久,用户体验差
- CPU计算能力不够,滑动会卡顿
- GPU渲染能力不够,页面会跳屏
- RAM内存容量不够,浏览器崩溃
- 统的方法是使用懒加载的方式,下拉到底部获取新的内容加载进来,其实就相当于是在垂直方向上的分页叠加功能,(但随着加载数据越来越多,浏览器的回流和重绘的开销将会越来越大)
虚拟列表的原理
虚拟列表的核心步骤可以总结成五步:
- 不把长列表数据一次性全部直接渲染在页面上
- 截取长列表一部分数据用来填充可视区域;
- 长列表数据不可视部分使用空白占位填充(下图中的startOffset和endOffset区域);
- 监听滚动事件根据滚动位置动态改变可视列表
- 监听滚动事件根据滚动位置动态改变空白填充
组件页面InfiniteScroll.vue
<template>
<div ref="container" class="list-container" @scroll="hScroll">
<div ref="content" class="list-content">
<slot v-for="item in showList" :info="item" :key="item"></slot>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
const container = ref(null)
const content = ref(null)
const props = defineProps(['list', 'itemHeight', 'showNum', 'start', 'end'])
const { list } = props
const itemHeight = ref(props.itemHeight)
const showNum = ref(list.length)
const start = ref(props.start)
const end = ref(props.end)
const initEnd = end.value
onMounted(() => {
container.value.style.height = itemHeight.value * showlist.value.length + 'px'
content.value.style.height = itemHeight.value * showNum.value + 'px'
})
const showList = computed(() => list.slice(start.value, end.value))
const hScroll = () => {
const scrollTop = container.value.scrollTop
start.value = Math.floor(scrollTop / itemHeight.value)
end.value = start.value + initend
if (end.value >= showNum.value) {
start.value = showNum.value - initend
end.value = showNum.value
}
content.value.style.transform = `translateY(${start, value * itemHeight.value}px)`
content.value.style.height = (itemHeight.value * showNum, value) - (start.value *
itemHeight.value) + 'px'
}
</script>
<style lang="less" scoped>
.list-container {
width: 400px;
overflow-y: scroll;
}
</style>
使用组件InfiniteScroll
的父级页面
<template>
<div>
<InfiniteScroll
itemHeight="21"
start="1"
end="16"
:list="list">
<template v-slot="{ info }">
<p>{{ info }}</p>
</template>
</InfiniteScroll>
</div>
</template>
<script setup>
import { ref } from 'vue';
import InfiniteScroll from "./InfiniteScroll.vue"
const list =ref(['你好1','你好2','你好3','你好4','你好5','你好6','你好7','你好8','你好9','你好10','你好11','你好12','你好13','你好14','你好15','你好16','你好17','你好18','你好19','你好20','你好21','你好22','你好23','你好24'])
</script>
<style>
</style>