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
相关产品推荐
相关产品推荐

