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

实现思路:

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>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A:好的,让我来为你编写一个无限滚动虚拟列表。 首先,我们需要一个容器元素,用于显示列表项。假设这个容器元素的高度为 500px,我们需要在其中循环显示列表项,并根据用户的滚动行为动态加载新的列表项。 下面是一种实现方式: HTML 代码: ``` <div id="list-container" style="height: 500px; overflow-y: scroll;"> <div id="list"> <!-- 列表项将在这里动态生成 --> </div> </div> ``` JavaScript 代码: ``` // 定义一个列表项的高度 const ITEM_HEIGHT = 50; // 定义一个存储所有列表项的数组 const items = []; // 定义一个函数,用于生成新的列表项,并添加到列表容器中 function renderItems(startIndex, endIndex) { const listEl = document.getElementById('list'); for (let i = startIndex; i <= endIndex; i++) { const itemEl = document.createElement('div'); itemEl.style.height = `${ITEM_HEIGHT}px`; itemEl.innerHTML = `第 ${i} 个列表项`; listEl.appendChild(itemEl); items.push(itemEl); } } // 初始化界面,显示初始数据 renderItems(0, 99); // 监听容器元素的滚动事件 const containerEl = document.getElementById('list-container'); let isLoading = false; // 当前是否正在加载新数据 let lastScrollTop = 0; // 记录上次滚动位置 containerEl.addEventListener('scroll', function() { const scrollTop = containerEl.scrollTop; const { clientHeight, scrollHeight } = containerEl; if (!isLoading && scrollTop > lastScrollTop && scrollTop + clientHeight >= scrollHeight - ITEM_HEIGHT * 10) { // 上拉加载更多数据 isLoading = true; const nextStartIndex = items.length; const nextEndIndex = nextStartIndex + 99; renderItems(nextStartIndex, nextEndIndex); isLoading = false; } else if (!isLoading && scrollTop < lastScrollTop && scrollTop <= ITEM_HEIGHT * 10) { // 下拉刷新数据 isLoading = true; const prevStartIndex = Math.max(0, items.length - 199); const prevEndIndex = prevStartIndex + 99; renderItems(prevStartIndex, prevEndIndex); containerEl.scrollTop = ITEM_HEIGHT * 100; isLoading = false; } lastScrollTop = scrollTop; }); ``` 上述代码实现了一个简单虚拟列表,它能够在用户滚动到底部时自动加载新的列表项,并在用户滚动到顶部时自动刷新显示前面的列表项。我们用一个名为 `items` 的数组存储了所有的列表项元素,这样,我们就可以避免不必要的 DOM 操作,提高了列表的渲染效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值