面试题答案
一键面试插值表达式导致性能问题的原因
- 脏检查机制:Angular 使用脏检查机制来检测数据变化。每当 Angular 进入摘要循环(例如用户交互、HTTP 请求完成等),它会检查所有绑定,包括插值表达式。大量的插值表达式意味着大量的检查,增加了摘要循环的时间和计算量。
- 频繁的变更检测:插值表达式绑定的数据一旦变化,就会触发脏检查,导致不必要的 DOM 更新。即使数据的变化不影响视图的实际显示,也会进行检查和潜在的更新操作。
优化方案及实现方式
- 使用
OnPush
变化检测策略- 原理:
OnPush
策略会告诉 Angular 只有在以下情况时才检查组件的变化:输入属性引用改变、组件接收到事件(如点击事件)、Observable 触发新值。这样可以减少不必要的脏检查,提高性能。 - 实现:在组件类中,通过设置
@Component
装饰器的changeDetection
属性为ChangeDetectionStrategy.OnPush
。例如:
- 原理:
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-large-list',
templateUrl: './large - list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LargeListComponent {
// 组件逻辑
}
- 虚拟滚动
- 原理:虚拟滚动只渲染当前视口内可见的列表项,而不是渲染全部大量的数据项,从而显著减少 DOM 元素数量,提升性能。
- 实现:可以使用 Angular 官方提供的
@angular/cdk/scrolling
库中的cdkVirtualFor
指令。 - 安装
@angular/cdk
:如果项目还未安装,在终端运行npm install @angular/cdk
。 - 在模块中导入:在相关的 Angular 模块(如
app.module.ts
)中导入ScrollingModule
。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform - browser';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule, ScrollingModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
- 在模板中使用:在组件的模板文件(如`large - list.component.html`)中,使用`cdkVirtualFor`代替普通的`*ngFor`。
<cdk - virtual - for - container [itemSize]="itemHeight" class="example - virtual - for - container">
<div *cdkVirtualFor="let item of largeDataList" class="example - item">
<!-- 这里插值显示数据项属性 -->
{{ item.property }}
</div>
</cdk - virtual - for - container>
在组件类中,需要定义itemHeight
变量(表示每个列表项的高度,单位像素)和largeDataList
数据源。
import { Component } from '@angular/core';
@Component({
selector: 'app - large - list',
templateUrl: './large - list.component.html'
})
export class LargeListComponent {
itemHeight = 50; // 每个列表项高度50px
largeDataList = Array.from({ length: 10000 }, (_, i) => ({ property: `Item ${i}` }));
}
- 不可变数据和纯管道
- 原理:使用不可变数据确保数据变化时生成新的对象引用,配合纯管道在插值表达式中处理数据。纯管道只有在输入值引用变化时才会重新计算,减少不必要的计算。
- 实现:
- 不可变数据:例如使用
immutable - js
库来创建和操作不可变数据。首先安装immutable - js
:npm install immutable
。在组件中:
- 不可变数据:例如使用
import { Component } from '@angular/core';
import { fromJS } from 'immutable';
@Component({
selector: 'app - large - list',
templateUrl: './large - list.component.html'
})
export class LargeListComponent {
largeDataList = fromJS([{ property: 'Initial value' }, { property: 'Another value' }]);
updateData() {
// 使用immutable - js更新数据,会返回新的对象引用
this.largeDataList = this.largeDataList.update(0, item => item.set('property', 'Updated value'));
}
}
- **纯管道**:创建一个纯管道,例如在`app.pipe.ts`中:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'dataPipe',
pure: true
})
export class DataPipe implements PipeTransform {
transform(value: any): any {
// 这里进行数据处理,如格式化等
return value;
}
}
在模板中使用:
<ul>
<li *ngFor="let item of largeDataList">{{ item.property | dataPipe }}</li>
</ul>