+
104
-

回答

我写了一个代码实现了uniapp的虚拟列表滚动加载,

代码说明:

totalItems:整个列表的数据,示例中用的是 1000 条数据。

visibleItemCount:设定在可视区域最多显示的条目数。

buffer:设定滚动缓冲区,这样可以在实际滚动时减少抖动。

visibleItems:计算当前需要显示的条目,截取 totalItems 的一部分。

topBlankHeight 和 bottomBlankHeight:计算上下两部分占位的空白高度

<template>
  <view class="container">
    <scroll-view
      class="scroll-view"
      scroll-y
      @scroll="onScroll"
      :scroll-top="scrollTop"
    >
      <!-- 占位的顶部空白区域 -->
      <view :style="{ height: topBlankHeight + 'px' }"></view>

      <!-- 实际显示的内容 -->
      <view
        v-for="(item, index) in visibleItems"
        :key="item.id"
        class="item"
      >
        {{ item.text }}
      </view>

      <!-- 占位的底部空白区域 -->
      <view :style="{ height: bottomBlankHeight + 'px' }"></view>
    </scroll-view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      totalItems: [], // 原始数据
      itemHeight: 60, // 每个item的高度
      visibleItemCount: 15, // 可视区域最多显示的item数量
      buffer: 10, // 缓冲区,较大缓冲减少抖动
      startIdx: 0, // 可视区域起始索引
      scrollTop: 0, // 滚动位置
    };
  },
  computed: {
    visibleItems() {
      return this.totalItems.slice(
        this.startIdx,
        this.startIdx + this.visibleItemCount + this.buffer
      );
    },
    topBlankHeight() {
      return this.startIdx * this.itemHeight;
    },
    bottomBlankHeight() {
      const remainingItems = this.totalItems.length - (this.startIdx + this.visibleItems.length);
      return remainingItems * this.itemHeight;
    },
  },
  methods: {
    onScroll: throttle(function (e) {
      const scrollTop = e.detail.scrollTop;
      const currentIdx = Math.floor(scrollTop / this.itemHeight);

      // 仅在距离足够大时更新startIdx,避免频繁更新
      if (Math.abs(currentIdx - this.startIdx) > this.buffer / 2) {
        this.startIdx = currentIdx - this.buffer / 2;
        this.startIdx = Math.max(0, this.startIdx);
      }

      this.scrollTop = scrollTop;
    }, 50), // 使用50ms的节流以减少频繁刷新
  },
  mounted() {
    // 初始化数据
    this.totalItems = Array.from({ length: 1000 }, (_, i) => ({
      id: i + 1,
      text: `Item ${i + 1}`,
    }));
  },
};

// 节流函数:限制函数在指定时间内只能执行一次
function throttle(func, delay) {
  let last = 0;
  return function (...args) {
    const now = new Date().getTime();
    if (now - last > delay) {
      last = now;
      return func.apply(this, args);
    }
  };
}
</script>

<style>
.container {
  height: 100vh;
}
.scroll-view {
  height: 100%;
}
.item {
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid #ddd;
}
</style>

网友回复

我知道答案,我要回答