Angular中*ngFor遍历函数返回对象数组导致MatSelect页面冻结问题
Angular Material MatSelect 使用函数返回对象数组导致应用冻结问题
问题描述
在使用Angular Material的MatSelect组件时,尝试用函数返回的对象数组作为*ngFor的迭代数据源,结果不仅功能失效,还导致Web应用冻结,控制台无任何报错信息。
Angular Material官网正常示例
HTML文件
<h4>基础下拉选择框</h4> <mat-form-field appearance="fill"> <mat-label>喜爱的食物</mat-label> <mat-select> <mat-option *ngFor="let food of foods" [value]="food.value"> {{food.viewValue}} </mat-option> </mat-select> </mat-form-field>
TS文件
import {Component} from '@angular/core'; interface Food { value: string; viewValue: string; } /** * @title 基础选择框 */ @Component({ selector: 'select-overview-example', templateUrl: 'select-overview-example.html', }) export class SelectOverviewExample { foods: Food[] = [ {value: 'steak-0', viewValue: '牛排'}, {value: 'pizza-1', viewValue: '披萨'}, {value: 'tacos-2', viewValue: '塔可'}, ]; }
失效尝试场景(应用冻结)
HTML文件
<h4>基础下拉选择框</h4> <mat-form-field appearance="fill"> <mat-label>喜爱的食物</mat-label> <mat-select> <mat-option *ngFor="let food of data()" [value]="food.value"> {{food.viewValue}} </mat-option> </mat-select> </mat-form-field>
TS文件
import {Component} from '@angular/core'; interface Food { value: string; viewValue: string; } /** * @title 基础选择框 */ @Component({ selector: 'select-overview-example', templateUrl: 'select-overview-example.html', }) export class SelectOverviewExample { foods: Food[] = [ {value: 'steak-0', viewValue: '牛排'}, {value: 'pizza-1', viewValue: '披萨'}, {value: 'tacos-2', viewValue: '塔可'} ]; data(): Food[] { return [ {value: 'steak-0', viewValue: '牛排'}, {value: 'pizza-1', viewValue: '披萨'}, {value: 'tacos-2', viewValue: '塔可'} ]; } }
补充信息
若函数返回字符串数组则可正常运行,示例如下:
HTML文件
<h4>基础下拉选择框</h4> <mat-form-field appearance="fill"> <mat-label>喜爱的食物</mat-label> <mat-select> <mat-option *ngFor="let food of data()" [value]="food"> {{food}} </mat-option> </mat-select> </mat-form-field>
TS文件
import {Component} from '@angular/core'; interface Food { value: string; viewValue: string; } /** * @title 基础选择框 */ @Component({ selector: 'select-overview-example', templateUrl: 'select-overview-example.html', }) export class SelectOverviewExample { foods: Food[] = [ {value: 'steak-0', viewValue: '牛排'}, {value: 'pizza-1', viewValue: '披萨'}, {value: 'tacos-2', viewValue: '塔可'}, ]; foods2 = ['牛排', '披萨', '塔可']; data(): string[] { return ['牛排', '披萨', '塔可']; } }
问题原因
Angular的变更检测机制会在每次检测周期调用模板中的data()函数。当函数返回对象数组时,每次调用都会创建全新的对象引用,Angular会判定数据持续变化,进而触发无限循环的变更检测,最终导致应用资源耗尽而冻结。
而返回字符串数组时,虽然每次调用也会创建新数组,但字符串是值类型,Angular能识别到数组内的实际值未发生变化,不会触发无限检测循环,因此可以正常运行。
解决方案
方案1:缓存数组到组件属性
将函数返回的数组缓存到组件的类属性中,模板直接引用该属性,避免每次变更检测都调用函数创建新对象:
// TS文件修改 export class SelectOverviewExample { // 缓存函数返回的数组 dataArray: Food[] = this.data(); foods: Food[] = [ {value: 'steak-0', viewValue: '牛排'}, {value: 'pizza-1', viewValue: '披萨'}, {value: 'tacos-2', viewValue: '塔可'} ]; data(): Food[] { return [ {value: 'steak-0', viewValue: '牛排'}, {value: 'pizza-1', viewValue: '披萨'}, {value: 'tacos-2', viewValue: '塔可'} ]; } }
<!-- HTML模板修改 --> <mat-option *ngFor="let food of dataArray" [value]="food.value"> {{food.viewValue}} </mat-option>
方案2:使用OnPush变更检测策略
给组件设置changeDetection: ChangeDetectionStrategy.OnPush,仅当输入属性变化或组件内部触发变更时才执行检测,减少不必要的检测循环:
import {Component, ChangeDetectionStrategy} from '@angular/core'; @Component({ selector: 'select-overview-example', templateUrl: 'select-overview-example.html', changeDetection: ChangeDetectionStrategy.OnPush // 添加此行 }) export class SelectOverviewExample { // ... 其余代码不变 }
方案3:使用纯管道封装函数逻辑
将数据生成逻辑封装成纯管道,纯管道仅在输入值变化时才重新计算,避免重复执行:
// 创建纯管道 import { Pipe, PipeTransform } from '@angular/core'; import { Food } from './select-overview-example'; @Pipe({ name: 'foodData', pure: true // 默认就是true,可显式声明 }) export class FoodDataPipe implements PipeTransform { transform(): Food[] { return [ {value: 'steak-0', viewValue: '牛排'}, {value: 'pizza-1', viewValue: '披萨'}, {value: 'tacos-2', viewValue: '塔可'} ]; } }
<!-- 模板中使用管道 --> <mat-option *ngFor="let food of [] | foodData" [value]="food.value"> {{food.viewValue}} </mat-option>
内容的提问来源于stack exchange,提问作者LPZadkiel
相关产品推荐
相关产品推荐

