含大量元素的组件销毁过慢问题及优化方案咨询
一、先把虚拟滚动配置弄对
你当前虚拟滚动的问题核心是itemSize不匹配实际行高,加上数据绑定方式不够高效:
itemSize必须与真实行高一致
这个参数是虚拟滚动计算可见区域的核心,要是写的60但实际行高不是60px,必然会出现空白或显示不全。如果你的行高是动态的(比如内容换行导致高度变化),别硬写固定值,改用CDK的自动尺寸策略:import { AutoSizeVirtualScrollStrategy } from '@angular/cdk/scrolling'; import { ViewChild } from '@angular/core'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; @ViewChild('scrollViewport') viewport!: CdkVirtualScrollViewport; ngAfterViewInit() { this.viewport.scrollStrategy = new AutoSizeVirtualScrollStrategy(); }该策略会自动计算每个列表项的实际高度,无需手动设置
itemSize。优化数据绑定与
trackBy函数
别直接遍历form.get('mainItemInfo')['controls'],频繁访问FormControl实例会额外消耗性能。建议维护一份独立的原始数据数组,通过索引关联表单控件:// 组件类内部 dataList = []; // 你的原始业务数据数组 get mainItemInfo() { return this.form.get('mainItemInfo') as FormArray; } tracker(index: number, item: any) { return item.id || index; // 优先用数据的唯一ID,无ID则用索引 }模板同步修改为:
<cdk-virtual-scroll-viewport #scrollViewport minBufferPx="600" maxBufferPx="1200" headerHeight="60" height="560px"> <div formArrayName="mainItemInfo" *cdkVirtualFor="let item of dataList; let i = index; trackBy: tracker;"> <div [formGroupName]="i"> <!-- 你的表单控件内容 --> </div> </div> </cdk-virtual-scroll-viewport>正确的
trackBy能让Angular只更新变化的项,避免不必要的全量重绘。调整缓冲区参数
minBufferPx和maxBufferPx别设太大,比如行高60px的话,minBufferPx=600(预加载10行)、maxBufferPx=1200(最多缓存20行)就足够,设6000会提前加载过多DOM,反而拖慢性能。
二、优化组件销毁速度
切换组件时卡顿,本质是销毁1000+个FormControl和DOM节点的开销过大,你可以在ngOnDestroy中做这些优化:
提前清空FormArray
组件销毁前,先清空表单数组的控件,减少需要销毁的FormControl实例数量:ngOnDestroy() { // 循环清空表单数组 while (this.mainItemInfo.length) { this.mainItemInfo.removeAt(0); } // 取消虚拟滚动相关的订阅(如果有) this.scrollSubscription?.unsubscribe(); }避免DOM操作阻塞
如果是路由切换组件,可以在离开前先隐藏虚拟滚动容器,让浏览器先处理DOM隐藏操作,再触发组件销毁:<div *ngIf="!isLeaving"> <cdk-virtual-scroll-viewport #scrollViewport ...> <!-- 虚拟滚动内容 --> </cdk-virtual-scroll-viewport> </div>然后在路由守卫或
ngOnDestroy执行前设置isLeaving = true,给浏览器一点处理时间,再销毁组件。清理行内组件的订阅
如果每个表单行内有单独的订阅(比如异步下拉框数据),一定要在每行组件的ngOnDestroy中取消订阅,既避免内存泄漏,也减少销毁时的额外开销。
额外提醒
- 别开启“显示1000条”的选项,虚拟滚动的核心价值就是只渲染可见区域DOM,强制显示1000条和不用虚拟滚动没区别,纯浪费性能。
- 如果行内包含复杂组件(日期选择器、富文本编辑器等),尽量做懒加载,只在该行进入视口时初始化组件。
内容的提问来源于stack exchange,提问作者George Petkov

