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

Observable<Object[]>实现无限滚动:能否用skip/take优化渲染?

解决Angular Observable无限滚动全量重渲染问题

你的思路完全正确——用skip+take的思路优化数据流,再配合trackBy,就能避免全量重渲染、解决页面跳顶的问题。先说说你当前代码的核心问题,再给你具体的实现方案:

为什么当前代码会导致页面跳顶?

你每次调用onScroll()时,都会让sales这个Observable重新返回包含所有已加载元素的全新数组(_.take(data, this.take))。Angular的*ngFor默认会对比数组的引用,一旦数组引用变化,就会销毁所有现有组件、重新创建整个列表——这不仅性能差,还会导致滚动位置丢失、页面跳回顶部。

优化方案:累积数据流 + trackBy

我们需要调整两个关键点:

  1. 用RxJS的scan操作符累积已加载的数据,每次只追加新条目,而非返回全量数组;
  2. 给*ngFor添加trackBy函数,让Angular能识别已有元素,避免重复渲染。

具体代码实现

第一步:添加trackBy函数(HTML + TS)

先给*ngFor加上trackBy,这是避免不必要重渲染的基础:

<div class="sales-container" infiniteScroll [infiniteScrollDistance]="2" [infiniteScrollThrottle]="50" (scrolled)="onScroll()">
  <app-sales-item 
    *ngFor="let sale of sales$ | async; trackBy: trackBySaleId" 
    [sale]="sale"
  ></app-sales-item>
</div>

在TS中实现trackBy(用销售记录的唯一ID作为标识,替换成你实际的字段):

trackBySaleId(index: number, sale: any): number {
  // 假设每条sale有唯一的id属性,根据你的实际数据调整
  return sale.id;
}

第二步:用RxJS重构无限滚动逻辑

我们用Subject触发加载事件,结合scan累积数据,确保每次只加载并追加新的10条:

import { Subject, switchMap, scan, startWith } from 'rxjs';

// 触发加载更多的信号源
private loadMoreTrigger$ = new Subject<void>();
// 每次加载的条数
private pageSize = 10;
// 初始加载条数
private initialLoadCount = 10;

public sales$: Observable<any[]>;

ngOnInit(): void {
  this.sales$ = this.filteringObservable.pipe(
    // 每次filteringObservable更新(比如筛选条件变化),重置加载状态
    switchMap(fullData => {
      // 先加载初始的10条
      const initialData = fullData.slice(0, this.initialLoadCount);
      
      // 监听加载更多信号,累积数据
      return this.loadMoreTrigger$.pipe(
        startWith(void 0), // 初始触发一次,确保加载第一页
        scan((loadedData, _) => {
          const currentLoadedCount = loadedData.length;
          // 如果已经加载完所有数据,直接返回现有列表
          if (currentLoadedCount >= fullData.length) {
            return loadedData;
          }
          // 计算要加载的新数据:从已加载的位置开始,取pageSize条
          const newData = fullData.slice(currentLoadedCount, currentLoadedCount + this.pageSize);
          // 追加新数据到已加载列表
          return [...loadedData, ...newData];
        }, initialData)
      );
    })
  );
}

onScroll(): void {
  // 触发加载更多
  this.loadMoreTrigger$.next();
}

方案说明

  • 累积数据流:通过scan操作符,我们每次只把新加载的条目追加到已有列表中,返回的数组是在原数组基础上扩展的(而非全新的全量数组),*ngFor只会渲染新增的元素;
  • trackBy保障:即使数据流更新,Angular会通过trackBy识别出已有元素,不会销毁重建这些组件,滚动位置自然不会丢失;
  • 适配实时更新:如果filteringObservable是实时更新的(比如后端推送新数据),当fullData变化时,switchMap会自动重置加载状态,重新加载初始数据,也能兼容后续的加载更多操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:04:05