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

Angular嵌套双ngFor列表实现CDK虚拟滚动(父元素独带滚动条)及性能优化

实现嵌套列表的CDK虚拟滚动(仅父元素显示滚动条)

1. 调整数据结构(适配虚拟滚动)

CDK虚拟滚动默认对扁平数组支持更友好,我们可以先把嵌套的组和用户项合并为一个统一的扁平数组,方便虚拟滚动计算渲染范围:

// 定义联合类型区分组和用户项
type ListItem = { type: 'group', data: OuterArrayItem } | { type: 'user', data: User };

// 组件内生成扁平化数组
flatListItems: ListItem[] = [];

ngOnInit() {
  this.flatListItems = this.OuterArray.reduce((acc, group) => {
    // 先加入组头
    acc.push({ type: 'group', data: group });
    // 再加入组内所有用户
    acc.push(...group.innerArray.map(user => ({ type: 'user', data: user })));
    return acc;
  }, [] as ListItem[]);
}

2. 替换嵌套循环为虚拟滚动结构

用CdkVirtualScrollViewport包裹整个列表,设置固定高度让滚动条仅出现在这个父容器上,再用*cdkVirtualFor遍历扁平化后的数组:

<mat-autocomplete>
  <!-- 虚拟滚动视口,设置固定高度,滚动条仅在这里显示 -->
  <cdk-virtual-scroll-viewport itemSize="48" class="virtual-scroll-container">
    <ng-container *cdkVirtualFor="let item of flatListItems; trackBy: trackByItem">
      <!-- 渲染组头 -->
      <mat-optgroup *ngIf="item.type === 'group'">
        <span class="d-flex-items-center">
          <mat-icon>group</mat-icon>
          <span>{{ item.data.groupName }}</span> <!-- 替换为你的组名称字段 -->
        </span>
      </mat-optgroup>
      <!-- 渲染用户项 -->
      <mat-option *ngIf="item.type === 'user'" [value]="item.data">
        <span>{{ item.data.userName }}</span> <!-- 替换为你的用户名称字段 -->
      </mat-option>
    </ng-container>
  </cdk-virtual-scroll-viewport>
</mat-autocomplete>

3. 样式控制滚动条范围

添加CSS确保滚动条仅在虚拟滚动视口上显示,内部元素不产生额外滚动:

.virtual-scroll-container {
  height: 400px; /* 根据需求设置固定/最大高度 */
  width: 100%;
  overflow-y: auto;
}

/* 禁止组和用户项内部出现滚动 */
mat-optgroup, mat-option {
  overflow: hidden;
}

4. 处理动态高度场景

如果组头和用户项高度不一致,不要用固定的itemSize,改用autoSize模式自动计算每个项的高度:

// 组件内引入视口实例触发初始计算
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
@ViewChild(CdkVirtualScrollViewport) viewport!: CdkVirtualScrollViewport;

ngAfterViewInit() {
  this.viewport.scrollToIndex(0); // 触发初始尺寸计算
}

修改模板:

<cdk-virtual-scroll-viewport autoSize class="virtual-scroll-container">
  <!-- 内容不变 -->
</cdk-virtual-scroll-viewport>
大数据集场景的性能优化方法
  • 强制使用trackBy函数:避免不必要的DOM重渲染,返回每个项的唯一标识:

    trackByItem(index: number, item: ListItem): string {
      return item.type === 'group' ? `group-${item.data.id}` : `user-${item.data.id}`;
    }
    
  • 延迟加载组内数据:初始只渲染组头,当用户展开组时再加载对应组的用户数据,减少初始渲染压力。

  • 调整虚拟滚动缓存:通过cdkVirtualFor的bufferSize属性设置预加载项数(比如bufferSize="5"),平衡滚动流畅度和内存占用。

  • 简化模板表达式:把复杂计算逻辑移到组件类中,用纯管道或属性处理,减少变更检测开销。

  • 启用OnPush变更检测:将组件的变更检测策略设为ChangeDetectionStrategy.OnPush,仅在输入属性变化或事件触发时执行检测。

  • 防抖输入过滤:如果列表和输入框绑定(如自动补全过滤),对输入事件做防抖处理,避免频繁过滤大数据集。

  • 结合分页逻辑:若虚拟滚动仍有性能瓶颈,可分批加载数据,滚动到底部时再加载下一批组和用户。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:40:32