Angular 6 MatTable渲染1000行性能卡顿,求优化方案
兄弟,你这个问题我太熟了——1000行MatTable全量渲染卡成狗,还碰到表头悬停就全量重渲的坑,用ChangeDetectionStrategy.OnPush绝对是对症下药的方案,而且完全可行!我给你一步步说怎么落地:
1. 给表格组件开启OnPush模式
首先在你的表格组件的@Component装饰器里配置变更检测策略,记得先导入对应的枚举:
import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'app-product-table', templateUrl: './product-table.component.html', styleUrls: ['./product-table.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush // 开启OnPush }) export class ProductTableComponent { // ... 你的组件逻辑 }
OnPush的核心逻辑是:只有当组件的输入属性引用发生变化、组件自身触发了事件(比如点击、输入),或者你手动触发变更检测时,Angular才会重新渲染这个组件,直接砍掉了大部分无意义的重渲。
2. 保证数据源的不可变性
因为OnPush认的是引用变化,所以更新表格数据的时候,绝对不能直接修改原数组(比如push、splice),必须返回一个新的数组引用。比如:
// 错误写法:修改原数组,OnPush不会识别 this.dataSource.data.push(newProduct); // 正确写法:返回新数组,触发OnPush的变更检测 this.dataSource.data = [...this.dataSource.data, newProduct]; // 或者用slice、filter等返回新数组的方法 this.dataSource.data = this.dataSource.data.filter(item => item.id !== deletedId);
如果用的是MatTableDataSource,直接给data赋值新数组就好,它内部会处理后续的表格更新。
3. 优化列模板的变更检测
如果你的列模板里有子组件(比如自定义的产品名称组件、操作按钮组件),记得给这些子组件也加上changeDetection: ChangeDetectionStrategy.OnPush,避免子组件自己触发不必要的重渲。
另外,模板里的表达式要尽量简化:
- 别在模板里写
{{ getProductStatus(product) }}这种动态计算的方法,每次变更检测都会执行一次。要么提前把计算好的status字段放到产品数据里,要么写一个纯管道来处理。 - 避免复杂的
ngIf嵌套,能拆成子组件的就拆,减少单模板的复杂度。
4. 解决表头悬停全量重渲的问题
你之前发现鼠标悬停表头时所有行都重渲,本质是因为默认的变更检测策略会遍历整个组件树,表头的悬停事件(哪怕只是触发了样式变化的事件)会触发父组件的变更检测,进而导致所有行组件都被检查一遍。
开了OnPush之后,只要行组件的输入属性(比如每一行的产品数据)没有变化,哪怕父组件触发了变更检测,行组件也不会重新渲染——完美解决这个悬停就全量重渲的坑!
额外的性能加成建议
OnPush已经能解决大部分问题,再配合这两个技巧,1000行甚至更多数据都能丝滑运行:
- 虚拟滚动:用
MatVirtualScroll配合MatTable,只渲染当前可视区域的行,1000行其实只渲染十几二十行,性能直接起飞。 - 设置trackBy函数:给MatTable的数据源设置trackBy,让Angular能复用已有的行组件,不用每次数据更新都销毁重建:
this.dataSource.trackBy = (index, product) => product.id; // 用产品唯一ID作为跟踪标识
内容的提问来源于stack exchange,提问作者mostafa cs

