Vue3【长列表-虚拟列表的实现】

546 阅读1分钟

image.png

众所周知,列表滚动加载这种需求很常见,特别是在移动webapp上。Github上各款移动端ui框架都会包含滚动加载功能的组件,但它们往往只提供很基础的功能,例如简单地监听滚动条并在满足条件的时候触发回调,然后通过某些方法把新的元素加入到页面末尾。这样的确可以解决分页加载数据的问题,但是在一个数据量比较大的情况下,页面元素会增加得很快,这时就会导致性能问题,想象一下,如果一个移动端页面上有1万条数据需要显示在页面的时候,是多么恐怖的事情

<template>
    <!--
        确认每一条数据的高度,可以确认可视区域内可以显示多少条数据,
        并且确认滚动时上去了几条,就要动态更新几条
     -->
    <div class="container" @scroll="handleSrcoll" ref="containerRef" :style="{ height: containerHeight }">
        <!-- 显示数据的地方:数据列表 -->
        <div class="list" :style="{ top: listTop }">
            <!-- 可视区域 -->
            <div v-for="item in showData" :key="item.id">
                {{ item.content }}
            </div>
        </div>
        <!-- 空白区域 -->
        <div class="bar" :style="{ height: barHeight }"></div>
    </div>
</template>

<script setup>
    // 要进行渲染的列表数据
    const list = reactive(new Array(10000).fill('').map((item,index)=>({
        id:index+1,
        content:`列表数据内容:${index+1}`
    })))
    // 每一条数据的高度
    const size = ref(40)
    // 每次渲染的数据条数
    const showNumber = ref(10)
    // 展示数据的起始下标
    const start = ref(0)
    // 展示数据的结束下标
    const end = showNumber
    // 最终显示的数据
    const showData = computed(()=>list.slice(unref(start), unref(end)))
    // 容器的高度
    const containerHeight = unref(size)*unref(showNumber)+'px'
    // 撑开高度,产生滚动
    let barHeight = computed(()=>unref(size)*list.length+'px')
    // 列表上移滚动的距离
    const listTop = computed(()=>{
        return unref(start)*unref(size)+'px'
    })
    // 容器的滚动事件
    const containerRef = ref(null)
    const handleSrcoll = ()=>{
        const scrollTop = containerRef.value.scrollTop
        console.log(scrollTop,'scrollTopscrollTop');
        // 起始下标
        start.value = Math.floor(scrollTop/size.value);
        // 结束下标
        end.value = start.value + 10
    }
    onUnmounted(() => {
        window.removeEventListener('scroll', handleSrcoll)
    })
</script>

<style scoped>
.container {
    overflow: scroll;
    background: rgba(150, 150, 150, .5);
    font-size: 20px;
    font-weight: bold;
    line-height: 40px;
    width: 500px;
    margin: 0 auto;
    position: relative;
    text-align: center;
}

.list {
    position: absolute;
    top: 0;
    width: 100%;
}
</style>

效果如下:

pwa.gif