长列表优化:简单实现个可以无限滚动的虚拟列表

news/2024/7/5 4:33:52

实现思路:

1、数据分片: 将要显示的数据单独拿出来,而不是将所有数据一次性加载到页面上。这可以减少页面渲染时的负担
2、可视区域管理: 只渲染用户当前可见的部分内容,即视口中的数据。当用户滚动列表时,动态地加载新的数据块进入视口,并移除离开视口的数据块
3、滚动事件监听: 用户滚动时加载新的渲染数据列表。可以根据滚动位置来计算当前应该显示哪些数据。滚动事件可以加上节流函数优化性能

Vue3 + TS 实例

<template>
  <div class="container" ref="containerRef">
    <ul class="list">
      <li v-for="(item, index) in visibleItems" :key="key ? item[key] : index" :style="`height: ${itemHeight}px;`" >
        <slot :item="item" :index="index" ></slot>
      </li>
      <div ref="bottomRef" ><slot name="bottom"></slot></div>
    </ul>
    <div class="placeholder" :style="`height: ${placeholderHeight}px;`" ></div>
  </div>
</template>

<script setup lang="ts">
  import { ref, toRefs, computed, onMounted } from 'vue';

  const props = defineProps({
    /** 列表数据 */
    data: {
      default: [],
      type: Array<any>
    },
    /** 列表项高度 */
    itemHeight: {
      default: 50,
      type: Number
    },
    /** 列表项唯一标识字段, 默认index */
    key: {
      default: '',
      type: String
    }
  })

  const { data, itemHeight } = toRefs(props)
  const containerRef = ref<HTMLElement | null>(null);
  const bottomRef = ref<HTMLElement | null>(null);
  const visibleHeight = ref(0) // 可视区域的高度

  onMounted(() => {
    visibleHeight.value = containerRef.value?.clientHeight || 0
    containerRef.value?.addEventListener('scroll', scrollEvent)
  })

  /** 渲染列表的数量 */
  const visibleNum = computed(() => {
    return visibleHeight.value / itemHeight.value * 2
  })

  /** 可视范围起始索引 */
  const startIndex = ref(0);
  /** 可视范围数据 */
  const visibleItems = computed(() => {
    return data.value.slice(startIndex.value, startIndex.value + visibleNum.value)
  })
  
  /** 整个列表的高度 */
  const placeholderHeight = computed(() => {
    const bottomRefHeight = bottomRef.value?.clientHeight || 0
    const bottomHeight = bottomRefHeight && itemHeight.value > bottomRefHeight ? itemHeight.value : bottomRefHeight
    return data.value.length * itemHeight.value - visibleHeight.value + bottomHeight + 7
  })

  /** 滚动事件 */
  const scrollEvent = (event: HTMLElementEventMap['scroll']) => {
    if(!event?.target) return
    const dom = event.target as HTMLElement
    const { scrollTop } = dom 
    startIndex.value = Math.floor(scrollTop / itemHeight.value)
    
    if(scrollTop <= 0 ) {
      return emits('touchTop')
    }
    if(scrollTop + 100 >= placeholderHeight.value ) {
      return emits('touchBottom')
    }
  }

  const emits = defineEmits(['touchTop', 'touchBottom'])
</script>

<style lang="less" scoped>
.container{
  width: 100%;
  height: 100%;
  overflow-y: auto;
  position: relative;
  &::-webkit-scrollbar {
    width: 8px;
    background-color: #dfdfdf;
  }
  &::-webkit-scrollbar-thumb {
    background: #999999;
  }
  &::-webkit-scrollbar-thumb:hover{
    background: #707070;
  }
}
.list{
  width: 100%; height: 100%;
  position: sticky;
  top: 0; left: 0;
  list-style: none;
}
</style>

http://lihuaxi.xjx100.cn/news/2161763.html

相关文章

智慧校园平台解决方案-报修管理系统

报修管理系统简介&#xff1a; 在数字化校园中报修管理系统也是后勤管理不可缺少的一个系统。校园资产在使用时间年限越来越长存在众多的物品需要维修&#xff0c;学生在住宿期间存在众多需要维系物品&#xff0c;那么在校园存在众多的报修场景下&#xff0c;学校的报修管理系统…

嵌入式ARM版本银河麒麟操作系统V10SP1安装OPenGauss数据库

前言&#xff1a; 官网提供了非常完整的openGauss安装步骤。 https://opengauss.org/zh/download/archive/列举一下个人的使用环境&#xff1a; 麒麟V10 rk3588工控板&#xff08;ARM&#xff09; openGauss-3.0.5&#xff08;极简版&#xff09;浏览一下官网&#xff0c;可以…

Django之rest_framework(二)

格式后缀 为了使我们的响应不再硬连接到单个内容类型这一事实,我们可以将API格式后缀添加到API之后。使用格式后缀为我们提供了明确引用给定格式的URL,譬如:http://example.com/api/items/4.json 官网:2 - Requests and responses - Django REST framework views:在函数…

转盘寿司---循环的转盘

寿司店周年庆&#xff0c;正在举办优惠活动回馈新老客户。 寿司转盘上总共有 n 盘寿司&#xff0c;prices[i] 是第 i 盘寿司的价格&#xff0c; 如果客户选择了第 i 盘寿司&#xff0c;寿司店免费赠送客户距离第 i 盘寿司最近的下一盘寿司 j&#xff0c;前提是 prices[j] <…

持续交付工具Argo CD的部署使用

Background CI/CD&#xff08;Continuous Integration/Continuous Deployment&#xff09;是一种软件开发流程&#xff0c;旨在通过自动化和持续集成的方式提高软件交付的效率和质量。它包括持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;两个主要阶…

中科软面试题

1、用户注册登录这一块用了哪些技术&#xff1f;数据库主要涉及那些表&#xff1f; 用了BCrypt加密算法&#xff0c;jwt生成token&#xff0c;网关实现全局过滤器校验token&#xff0c;还用了拦截器&#xff0c;获取在网关是指到请求头的userid存到threadlocal里面&#xff0c…

SOCKS代理是如何提高网络性能和兼容性的?

SOCKS代理作为一种网络协议中间件&#xff0c;不仅在提升网络隐私和安全性方面发挥着重要作用&#xff0c;也在提高网络性能和兼容性方面有着不容忽视的影响&#x1f680;。本文将深入探讨SOCKS代理如何通过减少网络延迟&#x1f680;、优化数据传输&#x1f504;、提高跨平台兼…

SpringBoot日志打印控制

按包控制日志输出&#xff1a; 我们有时候想在项目中控制某个包下面日志的输出等级&#xff0c;可以在yaml文件中这么写&#xff1a; #日志级别TRACE < DEBUG < INFO < WARN < ERROR < FATAL。 logging:level:com.atomikos: WARN