面试题答案
一键面试纯管道和非纯管道的区别
- 纯管道:
- 纯管道只在输入值发生纯变化时才会被调用。所谓纯变化,对于基本数据类型(如字符串、数字、布尔值),只要值改变就会触发;对于对象和数组,只有当引用改变时才会触发。
- Angular 会对纯管道的输入值进行缓存,若输入值未发生纯变化,Angular 不会重新调用管道,而是使用缓存结果。
- 非纯管道:
- 非纯管道在每次 Angular 检测到变化时都会被调用,无论输入值是否真正改变,包括视图渲染、用户交互、数据绑定变化等各种情况。
适用场景
- 纯管道适用场景:
- 当管道的计算逻辑相对简单,且输入值变化频率较低时适用。例如格式化日期、货币等。
- 示例:假设有一个格式化货币的管道
currencyPipe
,输入一个数字,返回格式化后的货币字符串。如果输入值不经常改变,使用纯管道就很合适。
@Pipe({ name: 'currencyPipe', pure: true }) export class CurrencyPipe implements PipeTransform { transform(value: number): string { return '$' + value.toFixed(2); } }
- 非纯管道适用场景:
- 当需要根据应用程序状态的任何变化(不仅仅是输入值变化)来更新输出时使用。例如根据当前用户语言动态格式化日期,因为语言可能随时在应用内切换,即使日期数据本身未改变,也需要重新格式化。
- 示例:假设要实现一个根据用户当前语言偏好动态格式化日期的管道。
@Pipe({ name: 'dynamicDatePipe', pure: false }) export class DynamicDatePipe implements PipeTransform { constructor(private dateService: DateService) {} transform(value: Date): string { const lang = this.dateService.getCurrentLang(); // 根据不同语言格式化日期 return value.toLocaleDateString(lang); } }
非纯管道带来更好效果的情况
- 实时数据展示:例如股票价格实时显示,股票价格数据对象的引用可能不会改变,但价格实时变化,使用非纯管道可以实时更新显示的价格。
- 基于应用程序状态变化:如上述根据用户语言偏好动态格式化日期的例子,语言偏好改变时,即使日期数据未变,也需要重新格式化。
非纯管道的性能问题及优化
- 性能问题:由于非纯管道在每次 Angular 变化检测时都会被调用,若管道内计算逻辑复杂,会导致性能开销较大,影响应用程序的响应速度。
- 优化方法:
- 减少不必要计算:在管道内部,添加逻辑判断,只有在真正需要时才进行复杂计算。例如在动态日期格式化管道中,判断语言是否改变,若未改变则不重新格式化。
@Pipe({ name: 'dynamicDatePipe', pure: false }) export class DynamicDatePipe implements PipeTransform { private lastLang: string; constructor(private dateService: DateService) {} transform(value: Date): string { const lang = this.dateService.getCurrentLang(); if (this.lastLang === lang) { return this.lastFormattedDate; } this.lastLang = lang; const formattedDate = value.toLocaleDateString(lang); this.lastFormattedDate = formattedDate; return formattedDate; } }
- 防抖和节流:使用 RxJS 的
debounceTime
或throttleTime
操作符来限制管道调用频率。例如在搜索框输入联想功能中,通过防抖减少非纯管道的调用次数。
import { Component } from '@angular/core'; import { Observable, fromEvent } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; @Component({ selector: 'app-search', templateUrl: './search.component.html' }) export class SearchComponent { searchText: string; constructor() { const input$ = fromEvent<HTMLInputElement>(document.getElementById('searchInput'), 'input'); input$.pipe( debounceTime(300) ).subscribe(() => { // 这里触发非纯管道调用,由于防抖,调用频率降低 }); } }