使用NgRx时如何高效更新Mat Table表格行?
我完全懂你的痛点——改一行的active状态却触发全表重绘,不仅浪费性能,连slide toggle的过渡动画都没了,确实让人抓狂。下面是几个关键优化点,一步步解决这个问题:
1. 让NgRx Reducer精准更新数据(核心)
首先要确保你的reducer只修改目标元素,保持数组其他元素的引用不变。如果reducer每次都返回一个全新的数组(比如用Array.from或者重新赋值),NgRx的selector会认为整个数据集都变了,导致Mat Table全量更新。
正确的reducer写法应该是用map遍历数组,只替换需要修改的元素:
// 示例Reducer export function periodicElementsReducer(state: State = initialState, action: PeriodicElementsActions): State { switch (action.type) { case ToggleActiveStatusAction.type: return { ...state, // 只更新目标ID对应的元素,其他元素保持原引用 periodicElements: state.periodicElements.map(element => element.id === action.id ? { ...element, active: !element.active } : element ) }; // 其他case... default: return state; } }
这样数组本身是新的,但只有被修改的元素是新引用,其他元素和之前完全一致,为后续优化打下基础。
2. 给Mat Table添加trackBy函数
这是Mat Table优化重绘的关键!默认情况下Angular会用数组索引来跟踪行,一旦数组引用变化(哪怕只有一行变了),所有行都会被视为“新行”重绘。我们要告诉Angular用元素的唯一ID来跟踪:
模板中添加trackBy
<table mat-table [dataSource]="dataSource" [trackBy]="trackById"> <!-- 你的列定义 --> <ng-container matColumnDef="active"> <th mat-header-cell *matHeaderCellDef> Active </th> <td mat-cell *matCellDef="let element"> <mat-slide-toggle [checked]="element.active" (change)="updateActiveStatus(element)" ></mat-slide-toggle> </td> </ng-container> <!-- 其他列... --> </table>
组件中实现trackById方法
trackById(index: number, element: PeriodicElement): number { return element.id; // 用唯一ID作为跟踪标识 }
现在Angular只会重绘ID匹配的那一行,其他行完全不动。
3. 启用组件的OnPush变更检测
把组件的变更检测策略设为OnPush,可以大幅减少不必要的变更检测触发,进一步提升性能:
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; @Component({ selector: 'app-periodic-table', templateUrl: './periodic-table.component.html', styleUrls: ['./periodic-table.component.css'], changeDetection: ChangeDetectionStrategy.OnPush // 启用OnPush }) export class PeriodicTableComponent implements OnInit { // 你的组件逻辑... }
因为我们用NgRx的Observable作为dataSource,OnPush会自动在Observable发出新值时触发变更检测,而且只检测真正变化的部分。
4. 确保Selector使用NgRx的缓存机制
用createSelector创建你的selector,它自带memoization(缓存)功能,只有当依赖的state片段变化时才会返回新值,避免不必要的数据流触发:
import { createSelector } from '@ngrx/store'; import { selectFeatureState } from './feature.selector'; export const selectPeriodicElements = createSelector( selectFeatureState, (state) => state.periodicElements );
这个selector只会在state.periodicElements的引用变化时才会发出新值,配合我们之前的reducer优化,完美契合Mat Table的更新逻辑。
最后验证效果
做完这些优化后,你再打开开发者工具的Performance面板,触发slide toggle的变更,会看到只有目标行的DOM发生了重绘,slide toggle的动画也能正常显示了。
本质上,这些优化都是围绕最小化数据变更范围和让Angular精准识别变化行这两个核心点展开的,既解决了性能问题,也修复了动画丢失的bug。
内容的提问来源于stack exchange,提问作者userradu

