虚拟列表 [10W条数据如何解决加载缓慢]

应用场景

后台一次性发送 10W 条或更多数据给前台,在接口不支持分页的情况下全部渲染,保证用户使用不卡顿。

问题分析

浏览器需要生成 10W 个 DOM 节点用来染页面,必然会导致页面的卡顿

解决方案

虚拟列表: 浏览器只需要生成少量 DOM 元素(节点数量取决于前端视图需要展示的数量),随着页面滚动,不断复用当前这一屏幕的 DOM 元素即可总结: 虚拟列表就是固定 DOM 节点数量,通过修改 DOM 元素的内容而达到不重新增加或删除 DOM 节点实现列表的更新。

实现原理

监听页面滚动,获取滚动的高度 scrollTop
根据 scrollTop 计算出当前应该展示哪一段数据,计算出 startlndex 和 endlndex根据当前展示的数据在长列表中的 index,对列表进行偏移

思路

1.封装组件InfiniteScroll
结构:
外层容器 >内容列表 >作用域插槽 slot

2.外层容器宽度固定,设置 overflow-y: scroll

3.接收参数

itemHeight: 每个条目的高度,用于动态列表的总高度
start: 开始的索引
end: 结束的索引
list: 超大数据,数组格式

4.onMounted 时计算外层容器高度和内容列表高度

5.定义计算属性 showList ,裁切 start - end 之间的数据

6.根据 showList 染 slot

7.监听外层容器的 scroll 事件

获取外层容器的 scrollTop
重新计算 start 和 end
start和 end 的边界外理
同时设置内容列表的 transform 和 height ,此消彼长

代码

InfiniteScroll.vue :

<template>
    <div ref="container" class="list-container" :style="{ height: props.scrollHeight + 'px' }" @scroll="handleScroll">
        <div ref="content" class="list-content">
            <slot v-for="item in showList" :info="item" :key="item"></slot>
        </div>
    </div>
</template>
<script setup>
import { computed, ref, defineProps } from 'vue'

const container = ref(null)
const content = ref(null)
const props = defineProps(['list', 'scrollHeight', 'start', 'end', 'itemHeight'])
const { list } = props
const itemHeight = ref(props.itemHeight)
const start = ref(props.start)
const end = ref(props.end)
const showLineNum = end.value
const showList = computed(() => list.slice(start.value, end.value))
const handleScroll = () => {
    const scrollTop = container.value.scrollTop
    start.value = Math.floor(scrollTop / itemHeight.value)
    end.value = start.value + showLineNum
    if (end.value >= list.length) {
        start.value = list.length - showLineNum
        end.value = list.length
    }
    content.value.style.transform = `translateY(${start.value * itemHeight.value}px)`
}
</script>
<style scoped>
.list-container {
    width: 400px;
    overflow-y: scroll;
}
</style>

App.vue :
 

<template>
  <InfiniteScroll :itemHeight="itemHeight" :scrollHeight="scrollHeight" :start="0" :end="showLineNum"
    :list="list">
    <template v-slot="{ info }">
      <div class="content" :style="{ height: height + 'px' }">{
   
   { info }}</div>
    </template>
  </InfiniteScroll>
</template>
<script setup>
import { ref, computed } from 'vue'
import InfiniteScroll from './components/InfiniteScroll.vue';

const list = ref([])
const itemHeight = ref(50)
const showLineNum = computed(() => {
  return Math.ceil(window.innerHeight / itemHeight.value) + 1
})
const scrollHeight = computed(() => window.innerHeight)
list.value = new Array(100000).fill(' ').map((item, index) => 'HelloWorld' + index)
</script>
<style>
body,
#app {
  height: 100vh;
  overflow: hidden;
}

.content {
  width: 100%;
  border: 2px solid black;
  line-height: 50px;
  text-indent: 10px;
  box-sizing: border-box;
}
</style>

猜你喜欢

转载自blog.csdn.net/m0_65121454/article/details/132738888