Angular 4中如何让*ngFor首次渲染后不再监听数组更新DOM?
这个问题我之前也碰到过,核心是Angular的变更检测机制和ngFor的默认行为在“搞事情”——模板里的tohide函数每次变更检测都会被执行,一旦返回值有变化,Angular就会触发视图更新,连带ngFor也会重新遍历数组。要实现“首次渲染后只控制显示隐藏、不修改原数组也不让*ngFor重新绑定”,可以试试这几个方案:
1. 给*ngFor添加trackBy函数,减少不必要的重渲染
*ngFor默认会对比数组的引用和元素的浅值,只要变更检测触发,哪怕数组本身没变化,它也可能重新渲染整个列表。添加trackBy后,它只会跟踪你指定的唯一标识,只有当这个标识变化时才会更新对应元素,而不是整个列表。
先在组件类中定义trackBy函数:
trackByImageIndex(index: number, image: string): number { return index; // 如果image本身是唯一值,也可以return image }
然后修改模板:
<div *ngFor='let image of images;let i = index; trackBy: trackByImageIndex'> <img [src]="'/assets/Images/'+image+'.png'" *ngIf="!tohide(i,0);"> </div>
这样即使tohide触发变更检测,*ngFor也不会重新绑定整个数组,只会检查每个元素的trackBy值,大大减少不必要的重渲染。
2. 把隐藏状态移到组件类的独立数组,避免模板调用函数
模板里直接调用函数是个容易踩的坑,因为每次变更检测都会执行它,很容易触发无意义的更新。更好的方式是在组件类中维护一个专门记录隐藏状态的数组,模板直接绑定这个状态:
组件类中初始化状态数组:
hiddenImages: boolean[] = []; ngOnInit() { // 首次渲染时初始化所有图片为显示状态 this.hiddenImages = this.images.map(() => false); // 这里执行你的隐藏逻辑,设置特定索引的图片状态 this.hiddenImages[0] = this.tohide(0, 0); } // 后续需要修改隐藏状态时,直接更新这个数组即可 toggleImage(index: number) { this.hiddenImages[index] = !this.hiddenImages[index]; }
模板修改为:
<div *ngFor='let image of images;let i = index; trackBy: trackByImageIndex'> <img [src]="'/assets/Images/'+image+'.png'" *ngIf="!hiddenImages[i]"> </div>
这样模板里没有函数调用,变更检测只会在hiddenImages数组变化时触发对应元素的显示隐藏,*ngFor完全不会因为隐藏逻辑重新绑定原数组。
3. 使用OnPush变更检测策略进一步优化
如果你的组件数据来源比较单一,可以把组件的变更检测策略设置为OnPush,这样组件只会在输入属性引用变化、组件内部触发事件或者手动调用变更检测时才更新,从根源上减少不必要的变更检测触发:
组件类配置:
import { Component, ChangeDetectionStrategy } from '@angular/core'; @Component({ selector: 'app-image-list', templateUrl: './image-list.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class ImageListComponent { // ... 其他业务代码 }
结合前面的trackBy和独立状态数组,这个方案能让组件的渲染性能达到最优,完全避免*ngFor的不必要重新绑定。
内容的提问来源于stack exchange,提问作者nOp

