web技术分享| 虚拟列表实现
Posted anyRTC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web技术分享| 虚拟列表实现相关的知识,希望对你有一定的参考价值。
针对过多数据列表展示造成过多节点渲染使页面卡死或卡顿,特地封装一个简易的虚拟列表,大家可在此基础上进行针对修改
组件基于 vue3 + element plus + ts + tailwindcss 开发
设计思路
分为三部分:
- 父容器占位;
- 一个子容器展示通过滚动条计算的数据相当于真实渲染节点;
- 一个子容器作为虚拟列表不渲染列表提供列表真实高度显示滚动条
实现
页面结构
<el-scrollbar
class="scrollbar_custom px-4"
:height="virtualRecord.height"
@scroll="handleScroll"
>
<!-- 虚拟高度 -->
<div
class="-z-10 absolute inset-0"
:style=" height: virtualRecord.virtualHeight + 'px' "
></div>
<!-- 真实列表 -->
<ul
:class="['absolute inset-0', prop.customClass]"
:style=" transform: `translateY($virtualRecord.offsetpx)` "
>
<li
v-for="(item, index) in virtualRecord.visibleData"
:key="index + '_'"
:style="
height: virtualRecord.itemHeight + 'px',
"
class="hover:bg-anyrtc-gray_8 text-sm"
>
<!-- 插槽 -->
<slot :item="item"></slot>
</li>
</ul>
</el-scrollbar>
样式
修改 element plus 的 scrollbar 样式
.scrollbar_custom
:deep(.el-scrollbar__wrap)
.el-scrollbar__view
@apply relative;
逻辑
组件所需参数
const prop = defineProps(
// 自定义类名
customClass: String,
// 相关配置
option:
type: Object,
default: () =>
return ;
,
,
listData:
type: Array,
default: () =>
return [];
,
,
);
组件内部定义
// 组件记录(默认)
const virtualRecord = reactive(
height: 400,
// 展示几个
visibleCount: 16,
// // 刷新频率
timeout: 4,
// // 行高
itemHeight: 40,
// translateY偏移量
offset: 0,
// 虚拟占位高度
virtualHeight: 300,
// 记录滚动高度
recordScrollTop: 0,
dataList: [] as any[],
// 可展示的数据
visibleData: [] as any[],
);
// 合并配置
const mergeFn = () =>
virtualRecord.height = prop.option.height || 400;
virtualRecord.dataList = JSON.parse(JSON.stringify(prop.listData));
virtualRecord.itemHeight = prop.option.itemHeight || 40;
virtualRecord.timeout = prop.option.timeout || 4;
// // 虚拟高度
virtualRecord.virtualHeight = prop.listData.length * virtualRecord.itemHeight;
// 展示数量
virtualRecord.visibleCount = Math.ceil(
virtualRecord.height / virtualRecord.itemHeight
);
;
滚动计算
let lastTime = 0;
const handleScroll = (scroll: scrollTop: number ) =>
let currentTime = +new Date();
if (currentTime - lastTime > virtualRecord.timeout)
virtualRecord.recordScrollTop = scroll.scrollTop;
updateVisibleData(scroll.scrollTop);
lastTime = currentTime;
;
const updateVisibleData = (scrollTop: number) =>
let start =
Math.floor(scrollTop / virtualRecord.itemHeight) -
Math.floor(virtualRecord.visibleCount / 2);
start = start < 0 ? 0 : start;
const end = start + virtualRecord.visibleCount * 2;
virtualRecord.visibleData = virtualRecord.dataList.slice(start, end);
virtualRecord.offset = start * virtualRecord.itemHeight;
;
列表信息变更
watch(
() =>
return [prop.listData, prop.option];
,
([newData, newHeight]) =>
if (newData)
// 合并数据
mergeFn();
// 更新视图
updateVisibleData(virtualRecord.recordScrollTop);
,
immediate: true,
);
使用
<VirtualItem :option=height:'占位高度' :list-data="列表" >
<template #default=" item ">
item
</template>
</VirtualItem>
以上是关于web技术分享| 虚拟列表实现的主要内容,如果未能解决你的问题,请参考以下文章