vue虚拟化列表(不确定单个项的高度)

63 阅读1分钟

1:模板

    <div
    id="list"
    class="list"
    >
        <div
          v-for="(msg, index) in listFilter"
          :key="index"
          class="item"
        >{{msg}}</div>
    </div>

2:收到数据

  props: {
     // 数据源
    msgList: { type: Array<String>, default: [] },
  },  

3:处理数据源

setup(props) {
    // 列表展示总数
    const sum = 16;
    // 增减的范围
    const addNum = 5;
    // 显示数据的区域范围
    const listLimit = reactive<{ start: number; end: number }>({
      start: 0,
      end: sum
    });
    // 同步数据源
    const listSource = ref<Array<string>>([]);
    watch(
      () => props.msgList,
      (val) => {
        listSource.value = val;
      },
      { immediate: true, deep: true }
    );
    // 根据范围截取展示的数据
    const listFilter = computed(() => listSource.value.slice(listLimit.start, listLimit.end));
    
    // 滑动事件
    const setCurrentScrollTop = (e: Event) => {
      const element = e.target as HTMLElement;
      // clientHeight 视口高度
      // scrollHeight整个dom的高度,
      // scrollTop当前的滑动高度:最大scrollHeight-clientHeight
      const { scrollTop, scrollHeight, clientHeight } = element;
       // 下滑处理
      if (scrollTop > scrollHeight - clientHeight - 20) {
        if (listLimit.end < props.msgList.length) {
          // 不能超过长度
          listLimit.end += addNum;
          listLimit.start += addNum;
          // 下滑到20时,触发滑动事件,还需反弹上来,继续触发事件
          element.scrollTo(0, scrollHeight - clientHeight - 30);
        }
      }
       // 上滑处理
      if (scrollTop < 20) {
        if (listLimit.start > 0) {
          listLimit.end -= addNum;
          // 处理边界
          listLimit.start = listLimit.start >= addNum ? listLimit.start - addNum : 0;
          element.scrollTo(0, 30);
        }
      }
    };
    onMounted(() => {
      const element = document.getElementById('list') as HTMLElement;
      element?.addEventListener('scroll', setCurrentScrollTop);
    });
    onUnmounted(() => {
      const element = document.getElementById('list') as HTMLElement;
      element?.removeEventListener('scroll', setCurrentScrollTop);
    });
    return { listFilter };
    }