定高虚拟列表:让大数据渲染变得轻松
定高虚拟列表
基本认识
在数据如潮水般涌来的今天,如何高效地展示和管理这些数据成为了开发者们面临的一大挑战,传统的列表渲染方式在处理大量数据时,往往会导致页面卡顿、滚动不流畅等问题,严重影响用户体验(在页面渲染大量的 dom 时,不仅是在第一次渲染比较影响性能外,在后续的每一次回流或重绘时,也会造成巨大的性能的问题) - 我们需要知道 JS 执行永远要比 DOM 快的多
然而,有一种技术能够在不牺牲用户体验的前提下,显著提升大数据集的处理效率,它就是——虚拟列表
虚拟列表 实际上是一种实现方案,只对 可视区域
进行渲染,对 非可视区域
中的区域不渲染或只渲染一部分(渲染的部分叫 缓冲区
,不渲染的部分叫 虚拟区
),从而达到极高的性能
虚拟列表也分为定高虚拟列表与非定高虚拟列表等,定高虚拟列表的意思就就是说列表中每一项的高度需要是一样的,反之不定高就是列表中的每一项高度都是不缺订单 - 我们这里先来学一下定高虚拟列表的实现方式(后续再继续更新不定高虚拟列表的实现方式)
我们可以先通过一张图来简单分析定高虚拟列表
-
-
从图中可以看出,我们可以将列表分为三个区域:可视区、缓冲区、虚拟区
-
而我们主要针对
可视区
和缓冲取
进行渲染
基本实现
这里采用 vue3 来实现,如果使用其它框架的或原生,对应实现思路基本也是差不多的
HTML 页面布局
-
容器元素 - 我们需要有一个可视窗口的容器,可以是一整个屏幕视口(也可以通过指定对应容器 .virtual-list-container 的高度,并使该元素作为可视区域)
-
占位区域 - 在容器中我们还需要一个占位元素,用于撑开容器的高度使其容器可以存在对应的滚动条(因为容器中只渲染可视区域中的元素,所以我们还需要该占位元素来根据列表的数据量来撑出对应滚动条的高度)
-
内容区域 - 最后我们还需要有一个可视区域的容器,这块部分为真正用户看到的列表区域,对应渲染的数据实际上是
可视区域
加缓存区域
的列表数据→ tip: 缓冲区的作用是防止快速下滑或者上滑的过程中出现空白区域
-
<!-- 容器元素 --> <div class="virtual-list-container"><!-- 占位元素 --><div class="virtual-list-placeholder"></div><!-- 渲染区域 --><div class="virtual-list-content"><!-- 子组件: 列表中的每一项 --></div> </div> + tip: 这里只是一个简单的结构,因为后续还有需要在这些结构中进行一些动态的修改
在 JS 中我们需要的状态
-
list - 列表数据(所有数据)
-
showList - 所要真正展示的数据(在所有数据列表
list
中进行slice
裁剪而得,根据下面的start
与end
进行个数的裁剪) -
itemHeight - 列表中每一项的高度
-
renderCount - 可视区可以渲染的项目数量(根据
可视区域的高度 / itemHeight
计算出来,因为基本都会有小数问题的,可以通过 Math 来进行处理一下) -
bufferCount - 缓存区的项目个数
-
start - 截取展示数据的开始索引
-
end - 截取展示数据的结束索引(根据
start + renderCount + bufferCount
获取,但是可能会超出整个列表,所有需要根据该计算出来的值需要根据list.length
取最小值,避免 end 超出列表的数据量) -
currentOffset - 滚动偏移量,因为可视区域渲染的元素的个数是基本固定的,顺着滚动条的滚动必然也会向上滚动,所以可以根据该值对可视区域进行相应偏移量的平移,避免随着滚动条滚动(根据
滚动大小 - (滚动大小 % itemHeight)
进行获取)
代码实现:
-
HTML 结构
-
<template><!-- 容器 --><div class="virtual-list-container" ref="virtual-list-container"><!-- 占位元素: 根据列表数据个数与每一项的大小计算出对应的高度 --><div class="virtual-list-placeholder" :style="{ height: placeholderHeight + 'px' }"></div><!-- 渲染区域: 根据对应的 currentOffset 偏移量,将渲染区域进行对应的平移 --><div class="virtual-list-content" :style="{ transform: `translateY(${state.currentOffset}px)` }"><!-- 子组件: 循环列表中的每一项 --><div class="item" v-for="item in showList" :key="item">{{ item }}</div></div></div> </template>
-
-
CSS 样式
-
<style scoped lang="scss"> .virtual-list {&-container { /* 容器大小 */width: 100%;height: 100vh;position: relative;overflow: auto;}&-placeholder {position: absolute;inset: 0;}&-content {position: absolute;inset: 0;} }.item { /* item 列表项样式 */outline: 1px solid orange;height: 60px;line-height: 60px;text-align: center; } </style>
-
-
JS 部分
-
<script setup> import { computed, onMounted, reactive, useTemplateRef } from 'vue';const list = new Array(20000).fill(null).map((item, i) => i + 1) // -- 模拟列表数据(所有数据) const containerRef = useTemplateRef("virtual-list-container") // -- 容器元素// -- 获取相应的状态 const state = reactive({start: 0, // -- 开始索引itemHeight: 60, // -- 每项 item 中的高度renderCount: 0, // -- 可视区域可以渲染的项目个数(根据容器高度与 itemHeight 进行计算)bufferCount: 6, // -- 缓存个数currentOffset: 0, // -- 根据滚动距离获取对应的偏移量 })const end = computed(() => // -- 计算结束索引Math.min(state.start + state.renderCount + state.bufferCount,list.length) )const showList = computed(() => list.slice(state.start, end.value)) // -- 根据 start 与 end 获取对应需要展示的列表数据onMounted(() => {const containerHeight = containerRef.value.offsetHeight // -- 获取容器高度state.renderCount = Math.round(containerHeight / state.itemHeight) // -- 获取可视区域可渲染个数 })const placeholderHeight = list.length * state.itemHeight // -- 占位元素的高度(用于给容器撑出对应的滚动条) </script>
-
-
上面的代码已经可以根据对应的视口大小,来计算所需要渲染的个数了,但是还有一个滚动时的处理,为了方便大家阅读,所以将滚动条滚动部分的代码放在这下面(这些代码直接与上面
JS 部分
进行合并即可)-
const handleScrollEvent = () => { // -- 触发滚动事件时,所需要处理的操作if (!containerRef.value) returnconst { scrollTop } = containerRef.value// -- 根据滚动大小,重新计算 start 开始索引(因为 end 结束索引是根据该 start 派生的计算属性,所以也会自动的进行计算)state.start = Math.round(scrollTop / state.itemHeight)// -- 根据滚动大小,重新计算对应滚动的偏移量,用于动态的给可视区域进行对应偏移量的平移state.currentOffset = scrollTop - (scrollTop & state.itemHeight) }onMounted(() => {if (!containerRef.value) returncontainerRef.value.addEventListener("scroll", handleScrollEvent) // -- 监听容器的滚动 })onUnmounted(() => {if (!containerRef.value) returncontainerRef.value.removeEventListener("scroll", handleScrollEvent) // -- 组件卸载时取消监听滚动事件 })
-
-
通过上面的代码我们就可以简单的实现了一个定高的虚拟列表了
注意:
- 上面的代码虽然实现了定高的虚拟列表,单滚动条的触发频率我们可以通过稍加一点点
节流(Throttle)
获防抖(Debounce)
来降低回调函数的触发频率(当然使用防抖还是节流具体看你想要的是一种怎么样的效果) 当然,我们也可以通过 Intersection Observer API 来代替滚动事件的监听,来提高对应的性能
总结: 定高虚拟列表实现思路
1. 在 HTML 结构中的处理
-
(1) - 需要有一个可视窗口的容器
-
(2) - 容器中需要有一个占位元素,用来撑开视口高度,使其能够有对应高度的滚动条(
该元素的高度需要等于: 长列表中的数据长度 * 每个元素的高度
→ tip: 如果元素存在 margin 等也需要计算上去
) -
(3) - 容器中还需要有一个内容区域(用来存放真正渲染的列表数据),该元素需要是一个根据容器元素的一个绝对定位元素(
用于当滚动条在滚动时,该内容区域元素可以根据定位中的 top 属性进行相应滚动距离的平移,避免整个容器区域也随着滚动上去(当然也可以通过 transform 进行平移)
)
2. 在 JS 中的处理
-
list - 列表数据(所有数据)
-
showList - 所要真正展示的数据(在所有数据列表
list
中进行slice
裁剪而得,根据下面的start
与end
进行个数的裁剪) -
itemHeight - 列表中每一项的高度
-
renderCount - 可视区可以渲染的项目数量(根据
可视区域的高度 / itemHeight
计算出来,因为基本都会有小数问题的,可以通过 Math 来进行处理一下) -
bufferCount - 缓存区的项目个数
-
start - 截取展示数据的开始索引
-
end - 截取展示数据的结束索引(根据
start + renderCount + bufferCount
获取,但是可能会超出整个列表,所有需要根据该计算出来的值需要根据list.length
取最小值,避免 end 超出列表的数据量) -
currentOffset - 滚动偏移量,因为可视区域渲染的元素的个数是基本固定的,顺着滚动条的滚动必然也会向上滚动,所以可以根据该值对可视区域进行相应偏移量的平移,避免随着滚动条滚动(根据
滚动大小 - (滚动大小 % itemHeight)
进行获取)
3. onscroll 监听滚动条 : 在滚动条监听的回调中,主要需要做如下两件事
- (1) - 获取当前的滚动大小,并根据该值与对应的 itemHeight 大小计算出新的 start,并赋值该对应的 start 状态中
- (2) - 根据滚动大小,动态设置 HTML 中的列表容器(.virtual-list)中的 top 样式,进行对应的平移 → 避免列表容器也随着滚动条的滚动而进行滚动
相关文章:

定高虚拟列表:让大数据渲染变得轻松
定高虚拟列表 基本认识 在数据如潮水般涌来的今天,如何高效地展示和管理这些数据成为了开发者们面临的一大挑战,传统的列表渲染方式在处理大量数据时,往往会导致页面卡顿、滚动不流畅等问题,严重影响用户体验(在页面…...

python request与grequests该如何选择
requests & grequests requests 和 grequests 是Python中用于发送HTTP请求的不同库。requests 是一个同步、阻塞式库,而 grequests 是基于 requests 封装的异步非阻塞库,它利用了 gevent 库提供的协程机制,能够并发发送多个请求。 选择…...

Unity3D UI 拖拽
Unity3D 实现 UI 元素拖拽功能。 UI 拖拽 通常画布上的 UI 元素都是固定位置的,我们可以通过实现拖拽接口,让 UI 元素可以被拖拽到其他位置。 拖拽接口 创建一个脚本 UIDrag.cs,在默认继承的 MonoBehaviour 后面,再继承三个接…...
介绍一下memcpy(c基础)
memcpy函数void *memcpy(void *dest, const void *src, size_t n); dest:指向目标内存区域的指针,即复制的目的地。src:指向源内存区域的指针,即要被复制的内容的来源。n:要复制的字节数 主要功能是将src所指向的内存…...

【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0
目录 一、相关面试题 1. HTTP 与 HTTPS 有哪些区别? 2. HTTPS 的工作原理?(https 是怎么建立连接的) (1)ClientHello (2)SeverHello (3)客户端回应 &a…...
python-23-一篇文章帮你理解Python推导式
python-23-一篇文章帮你理解Python推导式 一.简介 在 Python 中,推导式(Comprehensions)是一个简洁的语法,用于通过某种可迭代对象快速生成新的对象(如列表、字典、集合等!来开始我们今天的日拱一卒&…...

WPF中如何简单的使用CommunityToolkit.Mvvm创建一个项目并进行 增删改查
目录 开始前准备的数据库dbblog如下: 第一步:创建项目后下载四个NuGet程序包 第二步:删除原本的MainWindow.XAML文件 并创建如下的目录结构 然后在View文件夹下面创建Login.XAML和Main.XAML 并且在App.XAML中将启动项改为Login.X…...
CesiumJS 案例 P15:检测标记、鼠标点击移动标记、鼠标拖动标记
CesiumJS CesiumJS API:https://cesium.com/learn/cesiumjs/ref-doc/index.html CesiumJS 是一个开源的 JavaScript 库,它用于在网页中创建和控制 3D 地球仪(地图) 一、检测标记 <!DOCTYPE html> <html lang"en&…...

Webserver(4.9)本地套接字的通信
目录 本地套接字 本地套接字 TCP\UDP实现不同主机、网络通信 本地套接字实现本地的进程间的通信,类似的,一般采用TCP的通信流程 生成套接字文件 #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h&…...

[IAA系列] Image Aesthetic Assessment
Preface 本文旨在记录个人结合AI工具对IAA这个领域的一些了解,主要是通过论文阅读的方式加深对领域的了解。有什么问题,欢迎在评论区提出并讨论。 什么是IAA Image Aesthetic Assessment(图像美学评估)是一种评估图像在视觉上的…...

基于springboot的高校科研管理系统(源码+调试+LW)
项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据你想解决的问题,今天给…...

Flutter环境配置
配置环境变量 PUB_HOSTED_URLhttps://pub.flutter-io.cn FLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn 这个命令是用来配置 Flutter 的镜像源地址,主要是为了解决在中国大陆地区访问 Flutter 官方资源较慢的问题 具体的操作做如下: 右键点击"此…...

Rip动态路由及Rip动态路由优化
动态路由Rip Tip:Rip动态路由实现多个路由间不同网段通信。 本次实验目的,通过给ar1,ar2,ar3配置rip动态路由,实现pc1 ping通 pc2。 AR1配置如下: <Huawei>sy Enter system view, return user view with CtrlZ. [Huawei]…...
双路快速排序和三路排序算法
双路快速排序 一、概念及其介绍 双路快速排序算法是随机化快速排序的改进版本,partition 过程使用两个索引值(i、j)用来遍历数组,将 <v 的元素放在索引i所指向位置的左边,而将 >v 的元素放在索引j所指向位置的…...
SQL server增删改查语句和实例
在 SQL Server 中,增删改查操作分别对应 INSERT、DELETE、UPDATE 和 SELECT 语句。以下是具体介绍及实例: 一、插入数据(INSERT) 语法: INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, val…...

强化学习_06_pytorch-PPO2实践(ALE/Breakout-v5)
一、环境适当调整 数据收集:RecordEpisodeStatistics进行起始跳过n帧:baseSkipFrame一条生命结束记录为done:EpisodicLifeEnv得分处理成0或1:ClipRewardEnv叠帧: FrameStack 图像环境的基本操作,方便CNN捕捉智能体的行动 向量空间reset处理修…...

《JVM第8课》垃圾回收算法
文章目录 1.标记算法1.1 引用计数法1.2 可达性分析法 2.回收算法2.1 标记-清除算法(Mark-Sweep)2.2 复制算法(Coping)2.3 标记-整理算法(Mark-Compact) 3.三种垃圾回收算法的对比 为什么要进行垃圾回收&…...
SpringBoot整合Freemarker(二)
if分支 语法: <#if condition>... <#elseif condition2>... <#elseif condition3>... <#else>... </#if> 例子: <#if x 1>x is 1 </#if> --------------------------------- <#if x 1>x is 1 <…...

element plus el-form自定义验证输入框为纯数字函数
element plus 的el-form 使用自定义验证器,验证纯数字,禁止输入小数、中文、字母、特殊符号。input的maxlength为最大输入多少位长度 效果图 <el-form ref"dataFormRef" :model"dataForm" :rules"dataRules" label-w…...

Android笔记(三十一):Deeplink失效问题
背景 通过deeplink启动应用之后,没关闭应用的情况下,再次使用deeplink会失效的问题,是系统bug导致的。此bug仅在某些设备(Nexus 5X)上重现,launchMode并且仅当应用程序最初通过深层链接启动并再次通过深层…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...

uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...