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技术分享| 虚拟列表实现的主要内容,如果未能解决你的问题,请参考以下文章

web技术分享| 虚拟 tree

web技术分享| WebRTC控制摄像机平移倾斜和缩放

方物分享|服务器虚拟化稳定性技术概览

Linux:实现Hadoop集群Master无密码登录(SSH)各个子节点

web技术分享| WebRTC 实现屏幕共享

web技术分享| 实现WebRTC多个对等连接