写点什么

web 技术分享| 虚拟列表实现

作者:anyRTC开发者
  • 2022 年 9 月 14 日
    上海
  • 本文字数:1713 字

    阅读完需:约 6 分钟

针对过多数据列表展示造成过多节点渲染使页面卡死或卡顿,特地封装一个简易的虚拟列表,大家可在此基础上进行针对修改组件基于 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.offset}px)` }"    >      <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>
复制代码



发布于: 刚刚阅读数: 4
用户头像

实时交互,万物互联! 2020.08.10 加入

实时交互,万物互联,全球实时互动云服务商领跑者!

评论

发布
暂无评论
web技术分享| 虚拟列表实现_Vue_anyRTC开发者_InfoQ写作社区