虚拟列表,实战复制到项目中,解决几万条数据滚动卡顿

前言

  • 常网IT源码上线啦!
  • 本篇录入吊打面试官专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
  • 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
  • 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。

《爱在黎明破晓前》
我见了你一面,便用一生去怀念

三部曲讲解了青春、中年、老年的爱情,触动挺多的。

一、前言

今天像往常一样,开开心心的打代码编程。

突然产品说下载下拉选择有将近20000项,移动端点击选项比较卡。

于是便有了今天这篇文章,一起来探讨一下。

当然,你也可以当做面试题。

二、分析

在我们的移动端,表单通过配置渲染表单类型。

如果有兴趣的话,可以移步:Vue如何高效通过JSX动态渲染组件

而说回这次的下拉选择,一般选项不会超过100条,毕竟多了的话,也不好选择。

当然选项多了可以考虑,支持搜索功能。

那或许这又是另外一个问题。

  • 搜索调接口,在配置页添加接口url、入参query,即可远程搜索;

  • 又或者在这几万条数据中搜索过滤,都是可以配置选择。

如今我们需要解决如何在页面中显示这几万条数据选项。

很多人也习以为常搬出了虚拟列表。

在这之前,我们先来了解一下虚拟列表。

三、虚拟列表

虚拟列表是一种优化技术,仅对可见区域进行渲染,而非可见区域的数据则不渲染或部分渲染,从而减少资源消耗,提升用户体验。这种方法特别适合长列表,性能表现优异。

实现思路:

  1. 创建一个固定高度的 div,设置 overflow 以支持纵向滚动。
  2. 计算可视区域内可以显示的数据条数,即可视区域高度除以单条数据高度。
  3. 监听滚动事件,计算被卷起的数据的高度。
  4. 确定区域内数据的起始索引,通过卷起高度除以单条数据高度获得。
  5. 计算结束索引,即起始索引加上可显示的数据条数。
  6. 提取起始和结束索引之间的数据,渲染到可视区域。
  7. 设置起始索引在整个列表中的偏移位置,以更新列表内容。

不论如何滚动,只有滚动条的高度和可视区的元素内容会变化,始终保持渲染固定数量的元素,从而避免不必要的渲染开销。

对于刚接触的校友来说,可能有点小懵逼。

没关系,我们来看图。

dom元素

这是最外层的元素,设置死高度。

接着有一个元素是全部列表的数量*每一个项的高度(也就是完完整整数据的高度)

用父re子ab定位的形式,在计算top滚动了多少来定位

我计算这个高度(假如500px),每一个项50px,那一屏就加载10项

我一滚动,需要计算top和item(10项)

四、VirtualList

我们通过以上的图,大概明白了虚拟列表的实现,现在看看代码是如何实现的。

封装VirtualList组件

暴露了插槽slot,和listAll、itemHeight、contentHeight

 

javascript

代码解读

复制代码

<!-- * @Description: 虚拟列表 --> <template> <div :style="{ height: `${contentHeight}px` }" class="contentBox" @scroll="scroll"> <div :style="{ height: `${itemHeight * listAll.length}px`, position: 'relative' }"> <div :style="{ position: 'absolute', top: `${top}px`, width: '100%' }"> <div v-for="(item, index) in showList" :key="index" class="item"> <slot :item="item"></slot> </div> </div> </div> </div> </template> <script> export default { name: "list", props: { // 所有数据 listAll: { type: Array, default: () => [], }, // 每条数据所占高度 itemHeight: { type: Number, default: 50, }, //可视区域高度 contentHeight: { type: Number, default: 500, }, }, data() { return { showList: [], //可视区域显示的数据 showNum: 0, //可是区域显示的最大条数 top: 0, //偏移量 scrollTop: 0, //卷起的高度 startIndex: 0, //可视区域第一条数据的索引 endIndex: 0, //可视区域最后一条数据后面那条数据的的索引 }; }, methods: { getShowList() { this.showNum = Math.ceil(this.contentHeight / this.itemHeight); //可视区域最多出现的数据条数 this.startIndex = Math.floor(this.scrollTop / this.itemHeight); this.endIndex = this.startIndex + this.showNum; this.showList = this.listAll.slice(this.startIndex, this.endIndex); const offsetY = this.scrollTop - (this.scrollTop % this.itemHeight); this.top = offsetY; }, scroll() { this.scrollTop = document.querySelector(".contentBox").scrollTop; this.getShowList(); }, }, mounted() { this.scroll(); }, }; </script> <style scoped> .contentBox { overflow: auto; /*内容超出高度才会出现滚动条*/ } </style>

好的,我们看看父组件使用。

 

javascript

代码解读

复制代码

<van-radio-group v-model="result"> <van-cell-group> <VirtualList :listAll="list" :itemHeight="52" :contentHeight="500"> <template #default="{ item }"> <van-cell :key="item.label" :title="item.label" clickable> <template #right-icon> <van-radio :name="item.value" /> </template> </van-cell> </template> </VirtualList> <!-- <van-cell v-for="(item, index) in list" :key="index" :title="item.label" clickable > <template #right-icon> <van-radio :name="item.value" /> </template> </van-cell> --> </van-cell-group> </van-radio-group>

这是可以直接使用到项目中的,放心使用。

这样子实现后:

  1. 性能提升:只渲染可见区域,显著减少DOM操作,提升渲染速度。
  2. 内存节省:避免一次性加载所有数据,降低内存消耗。
  3. 流畅体验:用户滚动时响应迅速,提供平滑的浏览体验。
  4. 易于扩展:适用于大数据集,方便进行数据分页或无限滚动加载。

至此撒花~

后记

我们在实际项目中或多或少遇到一些奇奇怪怪的问题。

自己也会对一些写法的思考,为什么不行🤔,又为什么行了?

最后,祝君能拿下满意的offer。

我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车

👍 如果对您有帮助,您的点赞是我前进的润滑剂。

原文链接:https://juejin.cn/post/7418460243501629467

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值