美文网首页
vue3版虚拟滚动

vue3版虚拟滚动

作者: 一只小vivi | 来源:发表于2023-05-15 16:10 被阅读0次

    案例

    话不多说直接代码见

    父组件模版
    <template>
      <div
        class="scroll-list"
        :style="`height:${viewH}px;overflow-y: auto;`"
        @scroll="scrollHandler"
      >
        <div class="card-content" :style="`height:${scrollH}px;`">
          <div class="card_box" :style="`transform:translateY(${offsetY}px)`">
            <Card
              v-for="(item, index) in showList"
              :key="index"
              :itemSource="item"
              :index="index"
            ></Card>
          </div>
        </div>
      </div>
    </template>
    
    父组件上的逻辑代码
    <script setup>
    import Card from "@/components/scrollList/card.vue"; // 引入子组件
    import { pageQueryCompetitor } from "@/api/index.js";  //  后端接口引入
    import { onMounted, ref } from "vue";
    const sourceList = ref([]); // 所有数据
    const showList = ref([]); // 页面实际展示
    const viewH = ref(500); // 外层容器高度
    const itemH = ref(70); // 单个高度
    const scrollH = ref(0); // 滚动容器高度
    const showNum = ref(0); // 展示几个
    const lastTime = ref(0);
    const offsetY = ref(0); // 滚动偏移量
    const current = ref(1);
    const isLoading = ref(false); // 是否接口还在加载中
    const isFail = ref(false); // 请求是否失败
    const isEnd = ref(false); // 数据是否加载完了
    
    const scrollHandler = async (e) => {
      let newTime = new Date().getTime();
      let bottomH = itemH.value * showNum.value * 2;
    
      if (newTime - lastTime.value > 10) {
        let scrollTop = e.target.scrollTop;
        // 以1屏为基准进行偏移
        offsetY.value = scrollTop - (scrollTop % (itemH.value * showNum.value));
        // 触底判断
        let bool = e.target.scrollHeight - viewH.value - scrollTop > bottomH;
        if (!bool) {
          getSliceHandler(scrollTop);
          if (isLoading.value) return;
          if (!isFail.value && !isEnd.value) {
            current.value++;
          }
          isLoading.value = true;
          await getHttpHandler();
        } else {
          getSliceHandler(scrollTop);
        }
      }
    };
    // 展示内容进行截取
    const getSliceHandler = (scrollTop) => {
      let index = Math.floor(scrollTop / (itemH.value * showNum.value));
      showList.value = sourceList.value.slice(
        index * showNum.value,
        index * showNum.value + showNum.value * 2
      );
    };
    // 获取数据
    const getHttpHandler = async () => {
    // 传給后端的入参
      let params = {  
        current: current.value,
        eid: "4c8bd41b-2534-43c0-8643-5bf4dca1991e",
        pageSize: 30,
        queryType: 0,
        sortType: 0,
      };
      try {
        const { success, data } = await pageQueryCompetitor(params);
        if (success) {
          isFail.value = false;
          if (!data.records.length) {
            isEnd.value = true;
            return;
          }
          sourceList.value.push(...data.records);
          scrollH.value = sourceList.value.length * itemH.value;
          lastTime.value = new Date().getTime();
          isLoading.value = false;
        }
      } catch (e) {
        isFail.value = true;
        isLoading.value = false;
      }
    };
    
    onMounted(async () => {
      showNum.value = Math.floor(viewH.value / itemH.value) + 1;
      await getHttpHandler();
      showList.value = sourceList.value.slice(0, showNum.value * 2);
    });
    </script>
    
    子组件模版
    <template>
      <div class="card">
        <div class="item">{{ itemSource.competitor_name }}{{ index + 1 }}</div>
      </div>
    </template>
    
    <script setup>
    defineProps({
      itemSource: {
        type: Object,
        default: {},
      },
      index: {
        type: Number,
        default: 0,
      },
    });
    </script>
    
    <style lang="scss" scoped>
    .card {
      padding-bottom: 10px;
      .item {
        width: 400px;
        height: 60px;
        background-color: #008c8c;
        font-size: 14px;
      }
    }
    </style>
    

    相关文章

      网友评论

          本文标题:vue3版虚拟滚动

          本文链接:https://www.haomeiwen.com/subject/gcomsdtx.html