You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt.js电商项目实现滚动到底部自动加载商品列表方案

滚动到底部自动加载下一页实现方案

核心逻辑

实现滚动加载的核心是监听滚动事件,判断用户是否滚动到接近页面/列表容器底部的位置,触发下一页数据请求,并将新数据追加到现有列表中。同时要注意避免重复请求、处理无更多数据的场景。


一、原生JavaScript实现(全局页面滚动)

如果商品列表占满整个页面,监听window的滚动事件即可:

// 初始化分页参数
let currentPage = 1;
const pageSize = 10; // 你需要的N条记录数
let isLoading = false; // 加载锁,防止重复请求
let hasMore = true; // 是否还有更多数据

// 监听滚动事件
window.addEventListener('scroll', handleScroll);

function handleScroll() {
  // 加载中或无更多数据时直接返回
  if (isLoading || !hasMore) return;

  // 计算滚动位置:视口高度 + 已滚动距离 >= 文档总高度 - 偏移量(提前100px加载)
  const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
  const clientHeight = document.documentElement.clientHeight;
  const scrollHeight = document.documentElement.scrollHeight;

  if (scrollTop + clientHeight >= scrollHeight - 100) {
    loadNextPage();
  }
}

async function loadNextPage() {
  isLoading = true;
  try {
    // 调用后端接口获取下一页数据
    const res = await fetch(`/api/goods?page=${currentPage}&pageSize=${pageSize}`);
    const data = await res.json();

    if (data.list.length === 0) {
      hasMore = false;
      // 显示"已加载全部"提示
      document.getElementById('tip').textContent = '已加载全部商品';
      return;
    }

    // 将新数据追加到商品列表
    const goodsList = document.getElementById('goods-list');
    data.list.forEach(goods => {
      const item = document.createElement('div');
      item.className = 'goods-item';
      item.innerHTML = `<h3>${goods.name}</h3><p>¥${goods.price}</p>`;
      goodsList.appendChild(item);
    });

    currentPage++;
  } catch (err) {
    console.error('加载失败:', err);
    // 加载失败解锁,允许用户再次触发
    isLoading = false;
  } finally {
    isLoading = false;
  }
}

// 页面销毁时移除监听(比如单页应用切换路由)
// window.removeEventListener('scroll', handleScroll);

二、局部容器滚动(商品列表在固定高度容器内)

如果商品列表放在固定高度的滚动容器里,监听容器的scroll事件即可,计算逻辑类似:

const container = document.getElementById('goods-container');
container.addEventListener('scroll', handleContainerScroll);

function handleContainerScroll() {
  if (isLoading || !hasMore) return;

  // 容器滚动距离 + 容器视口高度 >= 容器内容总高度 - 偏移量
  if (container.scrollTop + container.clientHeight >= container.scrollHeight - 100) {
    loadNextPage();
  }
}

三、Vue项目实现示例

在Vue组件中结合响应式数据处理:

<template>
  <div class="goods-page">
    <div class="goods-list">
      <div v-for="item in goodsListData" :key="item.id" class="goods-item">
        <h3>{{ item.name }}</h3>
        <p>¥{{ item.price }}</p>
      </div>
    </div>
    <div class="tip" v-if="!hasMore">已加载全部商品</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      goodsListData: [],
      currentPage: 1,
      pageSize: 10,
      isLoading: false,
      hasMore: true
    };
  },
  mounted() {
    window.addEventListener('scroll', this.handleScroll);
    // 局部容器滚动的话,监听容器事件
    // this.$refs.goodsList.addEventListener('scroll', this.handleScroll);
  },
  beforeUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
    // this.$refs.goodsList.removeEventListener('scroll', this.handleScroll);
  },
  methods: {
    handleScroll() {
      if (this.isLoading || !this.hasMore) return;

      const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
      const clientHeight = document.documentElement.clientHeight;
      const scrollHeight = document.documentElement.scrollHeight;

      if (scrollTop + clientHeight >= scrollHeight - 100) {
        this.loadNextPage();
      }
    },
    async loadNextPage() {
      this.isLoading = true;
      try {
        const res = await this.$axios.get('/api/goods', {
          params: { page: this.currentPage, pageSize: this.pageSize }
        });
        const newData = res.data.list;
        if (newData.length === 0) {
          this.hasMore = false;
          return;
        }
        this.goodsListData = [...this.goodsListData, ...newData];
        this.currentPage++;
      } catch (err) {
        console.error('加载失败:', err);
      } finally {
        this.isLoading = false;
      }
    }
  }
};
</script>

四、React项目实现示例

在React中使用useEffect管理滚动监听:

import { useState, useEffect } from 'react';

function GoodsList() {
  const [goodsListData, setGoodsListData] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [isLoading, setIsLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const pageSize = 10;

  useEffect(() => {
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [isLoading, hasMore]);

  const handleScroll = () => {
    if (isLoading || !hasMore) return;

    const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    const clientHeight = document.documentElement.clientHeight;
    const scrollHeight = document.documentElement.scrollHeight;

    if (scrollTop + clientHeight >= scrollHeight - 100) {
      loadNextPage();
    }
  };

  const loadNextPage = async () => {
    setIsLoading(true);
    try {
      const res = await fetch(`/api/goods?page=${currentPage}&pageSize=${pageSize}`);
      const data = await res.json();
      const newData = data.list;
      if (newData.length === 0) {
        setHasMore(false);
        return;
      }
      setGoodsListData(prev => [...prev, ...newData]);
      setCurrentPage(prev => prev + 1);
    } catch (err) {
      console.error('加载失败:', err);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div className="goods-page">
      <div className="goods-list">
        {goodsListData.map(item => (
          <div key={item.id} className="goods-item">
            <h3>{item.name}</h3>
            <p>¥{item.price}</p>
          </div>
        ))}
      </div>
      {!hasMore && <div className="tip">已加载全部商品</div>}
    </div>
  );
}

export default GoodsList;

关键注意事项

  • 加载锁isLoading:必须添加,避免用户快速滚动触发多次重复请求,导致数据重复或接口报错。
  • 无更多数据处理:当接口返回空列表时,设置hasMore为false,停止触发请求并提示用户。
  • 偏移量调整:提前100px触发加载(scrollHeight - 100)可避免用户等待,提升体验,可根据需求调整数值。
  • 局部滚动适配:商品列表在固定高度容器内时,务必监听容器的scroll事件,而非window事件。

内容的提问来源于stack exchange,提问作者Krutarth Vora

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:43:17