vue优化滚动页面时的数据渲染性能:虚拟滚动(Virtual Scroll)

// src/hooks/useVirtualScroll.js
import { ref, onMounted, onUnmounted } from 'vue';

export function useVirtualScroll(itemHeight = 50, buffer = 5) {
  const containerRef = ref(null);
  const visibleData = ref([]);
  const totalHeight = ref(0);
  const startIndex = ref(0);
  const endIndex = ref(0);

  const updateVisibleItems = (scrollTop) => {
    const containerHeight = containerRef.value?.clientHeight || 0;
    startIndex.value = Math.max(0, Math.floor(scrollTop / itemHeight) - buffer);
    endIndex.value = Math.min(
      data.value.length,
      startIndex.value + Math.ceil(containerHeight / itemHeight) + buffer * 2
    );
    visibleData.value = data.value.slice(startIndex.value, endIndex.value);
    totalHeight.value = data.value.length * itemHeight;
  };

  const handleScroll = () => {
    if (!containerRef.value) return;
    const scrollTop = containerRef.value.scrollTop;
    updateVisibleItems(scrollTop);
  };

  onMounted(() => {
    containerRef.value?.addEventListener('scroll', handleScroll);
    updateVisibleItems(0);
  });

  onUnmounted(() => {
    containerRef.value?.removeEventListener('scroll', handleScroll);
  });

  return { containerRef, visibleData, totalHeight, startIndex };
}

这段代码实现了一个 ​虚拟滚动(Virtual Scroll)的 Vue3 Composition Hook,用于优化大数据量列表的渲染性能。以下是逐部分解析:


1. 核心功能

  • 解决的问题​:当渲染成千上万条数据时,直接渲染所有 DOM 节点会导致性能卡顿。
  • 实现思路​:
    只渲染当前可视区域内的数据项,用空白占位符撑起滚动条高度,模拟完整列表。

2. 参数说明

export function useVirtualScroll(itemHeight = 50, buffer = 5) {
  • itemHeight:每个列表项的固定高度(单位:px),默认 50px。
  • buffer:可视区域外的缓冲区项数(减少滚动时的空白闪烁),默认 5。

3. 关键变量

变量名类型作用
containerRefRef<HTMLElement>绑定滚动容器的 DOM 元素
visibleDataRef<Array>当前需要渲染的可见数据项(动态更新)
totalHeightRef<number>所有数据项的总高度(用于撑开滚动条)
startIndexRef<number>可视区域起始索引(动态计算)
endIndexRef<number>可视区域结束索引(动态计算)

4. 核心方法解析

​**​(1) updateVisibleItems(scrollTop)**​
const updateVisibleItems = (scrollTop) => {
  // 1. 获取容器高度
  const containerHeight = containerRef.value?.clientHeight || 0;

  // 2. 计算可见项的起始/结束索引(考虑缓冲区)
  startIndex.value = Math.max(0, Math.floor(scrollTop / itemHeight) - buffer);
  endIndex.value = Math.min(
    data.value.length,
    startIndex.value + Math.ceil(containerHeight / itemHeight) + buffer * 2
  );

  // 3. 更新当前需要渲染的数据
  visibleData.value = data.value.slice(startIndex.value, endIndex.value);

  // 4. 计算总高度(用于占位)
  totalHeight.value = data.value.length * itemHeight;
};
  • 计算逻辑​:
    • 根据滚动位置 scrollTop 和容器高度,动态计算哪些数据项应该被渲染。
    • buffer 的作用是提前加载可视区域外的部分数据,避免滚动时出现空白。
​**​(2) handleScroll()**​
const handleScroll = () => {
  if (!containerRef.value) return;
  const scrollTop = containerRef.value.scrollTop;
  updateVisibleItems(scrollTop); // 滚动时更新可见数据
};
  • 监听容器的 scroll 事件,实时更新可见数据。

5. 生命周期管理

onMounted(() => {
  containerRef.value?.addEventListener('scroll', handleScroll);
  updateVisibleItems(0); // 初始化时渲染首屏数据
});

onUnmounted(() => {
  containerRef.value?.removeEventListener('scroll', handleScroll);
});
  • 挂载时​:绑定滚动事件,初始化渲染。
  • 卸载时​:移除事件监听,避免内存泄漏。

6. 返回值

return { containerRef, visibleData, totalHeight, startIndex };
  • 暴露给组件的变量​:
    • containerRef:绑定到滚动容器的 ref
    • visibleData:当前需要渲染的数据子集。
    • totalHeight:所有数据的总高度(用于占位 div 的高度)。
    • startIndex:可视区域的起始索引(可用于调试)。

7. 在组件中的使用示例

<template>
  <div ref="containerRef" class="scroll-container">
    <!-- 占位元素(撑开滚动条) -->
    <div :style="{ height: `${totalHeight}px` }"></div>

    <!-- 实际渲染的可见数据 -->
    <div 
      class="visible-items" 
      :style="{ transform: `translateY(${startIndex * itemHeight}px)` }"
    >
      <div 
        v-for="item in visibleData" 
        :key="item.id" 
        class="item" 
        :style="{ height: `${itemHeight}px` }"
      >
        {{ item.content }}
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useVirtualScroll } from '@/hooks/useVirtualScroll';

// 模拟大数据
const data = ref(Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  content: `Item ${i + 1}`
})));

const itemHeight = 50; // 每项高度
const { containerRef, visibleData, totalHeight, startIndex } = 
  useVirtualScroll(itemHeight);
</script>

<style>
.scroll-container {
  height: 500px;
  overflow-y: auto;
  position: relative;
}
.visible-items {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}
.item {
  border-bottom: 1px solid #eee;
}
</style>

8. 性能优化点

  1. 固定高度​:要求所有列表项高度一致(动态高度需复杂计算)。
  2. 缓冲区(buffer)​​:减少滚动时的空白闪烁。
  3. 事件节流​:实际项目中可对 handleScroll 添加节流(如 lodash.throttle)。

总结

  • 输入​:itemHeight(项高度)和 buffer(缓冲区大小)。
  • 输出​:动态计算的 visibleData 和容器总高度 totalHeight
  • 核心思想​:
    用极少的 DOM 节点(仅可见区域 + 缓冲区)模拟完整长列表,大幅提升性能。

适用于任何需要渲染大数据量的 Vue3 项目(如表格、聊天记录、商品列表)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值