【vue】实现页面滚动加载

本文介绍了一种使用Vue.js实现滚动加载的优化技术,通过判断元素是否进入可视区来决定何时加载内容,减少初始页面加载负担。在`mounted`钩子中初始化数据,并监听滚动事件,利用`getBoundingClientRect()`方法判断元素位置,动态加载内容并模拟延迟请求,确保只有在需要时才加载数据。

页面内容太多会导致加载速度过慢,这时可考虑使用滚动加载即还没有出现在可视范围内的内容块先不加载,出现后再加载
基本思路就是判断元素是否出现在浏览器的可视区域,出现了就加载请求接口加载内容。但是要求内容块有最小高度。
具体代码如下


<template>
  <div>
    <div
      v-loading.fullscreen.lock="loading"
      class="page"
      id="tablist"
      @scroll="listScroll"
    >
      <div
        class="item"
        v-for="(item, i) in testData"
        :key="i"
        :class="i % 2 == 0 ? 'gray' : 'white'"
        :id="item.id"
      >
        {{ item.name }}
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "ScrollLoadingSample",
  props: {},
  data() {
    return {
      flag: true, // 开关
      loading: false, // loading加载
      testData: [], // 列表数据
      targetIndex: 0,
      nextId: "",
    };
  },
  mounted() {
    this.initData();
  },
  methods: {
    initData() {
      this.testData = [
        {
          id: "test-1",
          name: "区块1",
          // 要加载的方法名
          function: "loadTest1",
          // 方法是否加载了
          isLoaded: false,
        },
        {
          id: "test-2",
          name: "区块2",
          function: "loadTest2",
          isLoaded: false,
        },
        {
          id: "test-3",
          name: "区块3",
          function: "loadTest3",
          isLoaded: false,
        },
        {
          id: "test-4",
          name: "区块4",
          function: "loadTest4",
          isLoaded: false,
        },
        {
          id: "test-5",
          name: "区块5",
          function: "loadTest5",
          isLoaded: false,
        },
        {
          id: "test-6",
          name: "区块6",
          function: "loadTest6",
          isLoaded: false,
        },
      ];
      this.$nextTick(() => {
        this.loadPage();
      });
    },
    //判断元素是否在可视区域
    isElementInViewport(id) {
      let el = document.getElementById(id);
      //获取元素是否在可视区域
      let rect = el.getBoundingClientRect();
      return (
        rect.top + rect.height / 2 <=
          (window.innerHeight || document.documentElement.clientHeight) &&
        rect.bottom > 0
      );
    },

    // 初次加载页面时
    loadPage() {
      for (let i = 0; i < this.testData.length; i++) {
        if (this.isElementInViewport(this.testData[i]["id"])) {
          this.loadFunctionByName(this.testData[i]["function"]);
          this.testData[i]["isLoaded"] = true;
        } else {
          this.targetIndex = i;
          // 最开始没出现在页面上的id
          this.nextId = this.testData[this.targetIndex]["id"];
          break;
        } 
      }
    },
    // 页面滑动
    listScroll() {
      for (let i = this.targetIndex; i < this.testData.length; i++) {
        // 如果出现在页面上
        if (this.isElementInViewport(this.testData[i]["id"])) {
          // 如果方法没有加载
          if (!this.testData[i]["isLoaded"]) {
            this.loadFunctionByName(this.testData[i]["function"]);
            this.testData[i]["isLoaded"] = true;
          }
        } else {
          // 如果没有出现在页面上
          this.targetIndex = i;
          this.nextId = this.testData[this.targetIndex]["id"];
          break;
        }
      }
    },
    // 根据方法名加载
    loadFunctionByName(functionName) {
      switch (functionName) {
        case "loadTest1":
          this.loading = true;
          // 模拟延迟请求
          setTimeout(() => {
            console.log("加载区块1");
            this.loading = false;
          }, 1000);
          break;
        case "loadTest2":
          this.loading = true;
          // 模拟延迟请求
          setTimeout(() => {
            console.log("加载区块2");
            this.loading = false;
          }, 1000);
          break;
        case "loadTest3":
          this.loading = true;
          // 模拟延迟请求
          setTimeout(() => {
            console.log("加载区块3");
            this.loading = false;
          }, 1000);
          break;
        case "loadTest4":
          this.loading = true;
          // 模拟延迟请求
          setTimeout(() => {
            console.log("加载区块4");
            this.loading = false;
          }, 1000);
          break;
        case "loadTest5":
          this.loading = true;
          // 模拟延迟请求
          setTimeout(() => {
            console.log("加载区块5");
            this.loading = false;
          }, 1000);
          break;
        case "loadTest6":
          this.loading = true;
          // 模拟延迟请求
          setTimeout(() => {
            console.log("加载区块6");
            this.loading = false;
          }, 1000);
          break;
      }
    },
  },
};
</script>


<style scoped>
.page {
  width: 100vw;
  height: 100vh;
  overflow-y: auto;
  display: flex;
  flex-wrap: wrap;
  /* justify-content: center;
  align-items: center; */
}
.item {
  width: 100vw;
  height: 25vh;
  display: flex;
  justify-content: center;
  align-items: center;
}
.gray {
  background-color: #d5d1d1;
}
.white {
  background-color: white;
}
.loading {
  width: 80px;
  height: 100px;
  background: rgba(0, 0, 255, 0.664);
  display: inline-block;
}
</style>



需要注意 getBoundingClientRect()这个方法,正是通过这个方法获取元素位置,从而判断是否出现在可视区域

<think>好的,用户想要在Vue实现滚动加载商品的功能。首先,我需要回忆一下相关的Vue插件和实现方法。记得之前有看到关于vue-lazyload和无限滚动加载的引用内容,可能需要结合这些资源来给出解决方案。 用户提到的引用中有提到vue-lazyload插件,但那是针对图片懒加载的。不过引用[2]、[3]、[4]都涉及滚动加载数据,特别是无限滚动加载组件。比如引用[4]里用了infinite-loading插件,这可能是一个合适的工具。需要确认如何在Vue项目中安装和使用这个插件。 接下来,步骤应该是先安装插件,然后在组件中引入并配置。需要监听滚动事件,当用户滚动到底部时触发加载更多数据的函数。同时要处理数据加载的逻辑,比如分页请求API,并更新商品列表。还要注意在加载时显示加载状态,数据加载完毕后停止监听,或者显示没有更多数据。 可能需要考虑使用Vue3的组合式API,如引用[3]提到的,但用户的问题没有特别说明是Vue2还是Vue3,所以应该给出通用的方法,或者两者兼顾。不过示例可能需要用Vue3的写法,因为现在更多人转向Vue3了。 另外,引用[2]提到利用滚动加载防止大量渲染造成的卡顿,这点需要注意性能优化,比如使用v-for时的key属性,或者可能的虚拟滚动技术,但基础示例可能不需要那么复杂。 需要确保代码示例正确,比如使用infinite-loading组件,处理加载函数,管理分页参数,以及处理API请求的异步操作。同时,要处理错误情况,比如网络请求失败时的重试或提示。 最后,生成相关问题,可能涉及性能优化、不同插件的比较、如何结合搜索功能等,这样用户可以有进一步的参考。</think>### Vue实现滚动加载商品功能教程 在Vue实现滚动加载商品功能,可通过以下步骤结合`vue-infinite-loading`插件完成: --- #### 1. 安装插件 ```bash npm install vue-infinite-loading --save ``` --- #### 2. 基础实现代码示例 ```vue <template> <div> <!-- 商品列表 --> <div v-for="item in items" :key="item.id" class="product-item"> <h3>{{ item.name }}</h3> <p>价格:{{ item.price }}</p> </div> <!-- 无限滚动加载组件 --> <InfiniteLoading @infinite="loadMore" :distance="100"> <template #spinner> 加载... </template> <template #no-more> 没有更多商品了 </template> <template #no-results> 暂无数据 </template> </InfiniteLoading> </div> </template> <script> import { ref } from 'vue'; import InfiniteLoading from 'vue-infinite-loading'; export default { components: { InfiniteLoading }, setup() { const items = ref([]); // 存储商品数据 const page = ref(1); // 当前页码 const hasMore = ref(true); // 是否还有更多数据 // 模拟API请求 const fetchData = async () => { try { const response = await axios.get('/api/products', { params: { page: page.value } }); return response.data; } catch (error) { console.error('加载失败:', error); return []; } }; // 滚动加载触发的方法 const loadMore = async ($state) => { const newItems = await fetchData(); if (newItems.length > 0) { items.value.push(...newItems); page.value++; $state.loaded(); // 通知插件加载完成 } else { hasMore.value = false; $state.complete(); // 通知插件无更多数据 } }; return { items, loadMore }; } }; </script> ``` --- #### 3. 核心实现逻辑 1. **滚动监听**:通过`vue-infinite-loading`组件自动监听滚动事件,当滚动到指定阈值(`distance`参数)时触发`loadMore`方法[^4]。 2. **分页请求**:使用`page`变量记录当前页码,每次请求递增页码。 3. **数据合并**:将新加载的数据通过`items.value.push(...newItems)`合并到现有列表中。 4. **状态控制**:通过`$state.loaded()`和`$state.complete()`控制加载状态提示。 --- #### 4. 优化建议 - **性能优化**:对长列表使用`<div v-for>`时添加唯一`:key`,或结合虚拟滚动技术(如`vue-virtual-scroller`)[^2]。 - **错误处理**:在`fetchData`中增加重试机制或用户提示。 - **搜索兼容**:若页面有搜索功能,重置`page=1`并清空`items`后再触发加载。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值