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

Angular中HostListener监听滚动触底如何仅触发一次加载逻辑

问题原因

document:scroll是高频触发事件,用户滚动过程中每一次微小的页面位移都会触发回调执行。当前实现没有加加载状态控制,只要滚动位置满足触底条件,就会重复执行加载逻辑,前一批数据的请求还没返回时,后续触发的回调又会发起新的加载请求,最终导致所有数据被一次性拉取完成。

修复方案

核心是增加加载状态锁作为拦截条件,配合「无更多数据」的终止判断,从根本上避免重复触发加载,可选增加防抖优化滚动性能:

  • 新增两个控制变量:isLoading标记当前是否处于数据加载中,isNoMoreData标记是否已经加载完全部数据、没有下一页内容
  • 滚动回调执行时先判断状态:如果正在加载、或者已经没有更多数据,直接终止逻辑不做后续判断
  • 确认满足触底条件、准备发起加载请求前,先把isLoading设为true上锁,直到本次请求完成(成功/失败都算)再把锁解开
  • 每次加载完数据后判断返回结果,如果返回的数据条数小于单页加载条数,就把isNoMoreData设为true,后续不再触发加载逻辑
  • 可选优化:给滚动回调加200-300ms的防抖,减少高频位置计算带来的性能消耗

改造后参考代码

import { HostListener, Component, OnInit } from '@angular/core';
import { debounceTime, fromEvent } from 'rxjs';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html'
})
export class ListComponent implements OnInit {
  // 列表数据
  list: any[] = [];
  // 加载状态锁
  isLoading = false;
  // 无更多数据标记
  isNoMoreData = false;
  // 分页参数
  currentPage = 1;
  pageSize = 20;

  ngOnInit() {
    // 可选:用rxjs给滚动事件加300ms防抖,降低触发频率
    fromEvent(window, 'scroll')
      .pipe(debounceTime(300))
      .subscribe(() => this.handleScroll());
  }

  // 如果不用上面的fromEvent防抖,直接保留@HostListener绑定即可
  // @HostListener('document:scroll', [])
  handleScroll() {
    // 状态拦截:正在加载/无更多数据时直接返回
    if (this.isLoading || this.isNoMoreData) return;

    // 触底判断:距离底部100px时就触发加载,优化用户体验
    const isReachBottom = window.innerHeight + window.scrollY >= document.body.offsetHeight - 100;
    if (isReachBottom) {
      this.loadNextPageData();
    }
  }

  loadNextPageData() {
    // 发起请求前先上锁
    this.isLoading = true;

    // 替换成你实际的接口请求逻辑
    this.dataService.getList(this.currentPage, this.pageSize).subscribe({
      next: (res) => {
        this.list.push(...res.data);
        this.currentPage++;
        // 返回数据不足一页时,标记为无更多数据
        if (res.data.length < this.pageSize) {
          this.isNoMoreData = true;
        }
      },
      error: (err) => {
        console.error('数据加载失败', err);
      },
      complete: () => {
        // 请求结束后无论成功失败都解锁
        this.isLoading = false;
      }
    })
  }
}

注意:防抖只能降低滚动回调的执行频率,无法完全避免重复触发,比如用户滚动停在触底阈值区间时,防抖后的回调依然可能多次执行,加载状态锁是必须实现的核心逻辑,不能只靠防抖解决问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.21 16:16:02