Angular Material Table性能优化:动态列切换重渲染慢及列添加动画实现
我之前也碰到过类似的Angular Material表格动态列切换性能瓶颈,尤其是数据量过百行的时候,全量重渲染确实会让人头疼。结合实际项目经验,给你整理几个可行的方案:
一、性能优化方案:避免全量重渲染
1. 用trackBy函数减少行重渲染
当你修改displayColumns时,Angular会默认销毁并重建所有表格行的DOM节点——这就是耗时3秒的核心原因。给表格行添加trackBy函数,让Angular只更新真正变化的行,而不是全量重建:
首先在组件类中定义trackBy函数(用行数据的唯一标识,比如id,不要用索引):
trackByRowId(index: number, row: YourRowType): number { return row.id; // 替换成你行数据的唯一键 }
然后修改模板中的行定义:
<tr mat-row *matRowDef="let row; columns: displayColumns; trackBy: trackByRowId"></tr>
这个改动能大幅减少不必要的DOM操作,尤其是行数据本身没有变化,只是列显示切换的场景。
2. 用display: none替代修改displayColumns(推荐高频切换场景)
如果你只是切换列的显示/隐藏,而不是真正添加/删除列,完全可以用CSS控制显示隐藏,而非修改displayColumns。这样表格的DOM结构不会变化,也就不会触发全量重渲染:
比如先把所有可能的列都加入displayColumns,然后给每个列的表头和单元格添加显示控制:
<ng-container matColumnDef="username"> <th mat-header-cell *matHeaderCellDef [style.display]="showUsername ? 'table-cell' : 'none'"> 用户名 </th> <td mat-cell *matCellDef="let row" [style.display]="showUsername ? 'table-cell' : 'none'"> {{row.username}} </td> </ng-container>
这种方式的性能提升非常明显,因为只是修改样式属性,而非重建DOM。唯一的小代价是DOM节点会多一些,但对于100+行的表格来说,这个代价远比重渲染的耗时小。
3. 虚拟滚动(适合大数据量场景)
如果以后数据量还会增长,建议用上Angular CDK的虚拟滚动,让表格只渲染当前可见区域的行,彻底解决大量行的重渲染问题:
<cdk-virtual-scroll-viewport itemSize="50" class="table-viewport"> <table mat-table [dataSource]="dataSource"> <!-- 列定义 --> <tr mat-header-row *matHeaderRowDef="displayColumns; sticky: true"></tr> <tr mat-row *matRowDef="let row; columns: displayColumns; trackBy: trackByRowId"></tr> </table> </cdk-virtual-scroll-viewport>
记得在组件中导入ScrollingModule,并给虚拟滚动容器设置合适的高度。
二、实现列添加的动画效果
不管是用display: none切换显示,还是动态添加列,都可以用Angular的动画模块实现平滑过渡:
1. 列显示/隐藏的切换动画
先在组件中定义动画:
import { animate, state, style, transition, trigger } from '@angular/animations'; @Component({ selector: 'your-table-component', templateUrl: './your-table.component.html', styleUrls: ['./your-table.component.css'], animations: [ trigger('columnToggle', [ state('hidden', style({ opacity: 0, width: '0px', padding: '0' })), state('visible', style({ opacity: 1, width: '*', padding: '0 16px' // 匹配Material表格的默认内边距 })), transition('hidden <=> visible', animate('300ms ease-in-out')) ]) ] })
然后把动画绑定到列的表头和单元格:
<ng-container matColumnDef="username"> <th mat-header-cell *matHeaderCellDef [@columnToggle]="showUsername ? 'visible' : 'hidden'"> 用户名 </th> <td mat-cell *matCellDef="let row" [@columnToggle]="showUsername ? 'visible' : 'hidden'"> {{row.username}} </td> </ng-container>
这样切换列显示时,会有淡入淡出+宽度变化的平滑动画。
2. 动态添加列的入场动画
如果是动态新增列(比如从后端获取新列并添加到表格),可以给新列添加入场动画:
// 在组件的动画数组中添加 trigger('columnEnter', [ transition(':enter', [ style({ opacity: 0, width: '0px' }), animate('300ms ease-in-out', style({ opacity: 1, width: '*' })) ]) ])
然后在动态生成的列模板中使用:
<ng-container *ngFor="let col of dynamicColumns" [matColumnDef]="col.key"> <th mat-header-cell *matHeaderCellDef [@columnEnter]> {{col.label}} </th> <td mat-cell *matCellDef="let row" [@columnEnter]> {{row[col.key]}} </td> </ng-container>
新添加的列会从无到有平滑展开,体验更好。
内容的提问来源于stack exchange,提问作者Alex Sushkp

