如何仅向Angular组件传递一次数据,避免@Input持续监听触发重复调用
问题原因说明
你当前的实现之所以会反复调用getImages,是因为Angular的变更检测机制会在每次检测周期中重新计算模板内的所有表达式,你直接把方法调用getImages('cats')作为输入绑定值,每次变更检测都会触发该方法执行,自然会反复生成新的配置对象、发起重复的API请求。另外你原有的Scroller组件代码里有个笔误,then(images => this.movies = movies)应该改为this.movies = images,否则无法正确拿到图片数组。
下面是两种无需使用@Input()的实现方案,都可以保证数据仅传递一次:
方案1:父组件通过@ViewChild手动赋值
父组件通过视图查询拿到Scroller组件的实例,在视图初始化完成后手动给子组件传递配置,全程仅执行一次赋值:
父组件代码
import { Component, ViewChild, AfterViewInit } from '@angular/core'; import { ScrollerComponent } from './scroller.component'; @Component({ selector: 'app-random', template: ` <app-scroller #catScroller></app-scroller> <app-scroller #dogScroller></app-scroller> ` }) export class RandomComponent implements AfterViewInit { @ViewChild('catScroller', {static: true}) catScroller!: ScrollerComponent; @ViewChild('dogScroller', {static: true}) dogScroller!: ScrollerComponent; constructor(private imageService: ImageService) {} ngAfterViewInit() { // 仅在视图初始化时执行一次赋值,不会反复调用 this.catScroller.init = this.getImages('cats'); this.dogScroller.init = this.getImages('dogs'); } getImages(src: string) { return { aspect: '16/9', res: 'min', sources: this.imageService.getImagesFromAPI(src) }; } }
Scroller组件代码
只需要把原来的@Input()装饰器去掉即可,其他逻辑不需要修改:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-scroller', template: `<!-- 原有组件模板 -->` }) export class ScrollerComponent implements OnInit { public movies: string[] = []; // 去掉@Input()装饰器,改为公开属性供父组件直接赋值 public init!: {aspect: string, res: string, sources: Promise<string[]>}; ngOnInit() { this.init.sources.then(images => this.movies = images); } }
方案2:使用依赖注入令牌传递配置
如果你想更符合Angular的依赖注入规范,也可以通过注入令牌的方式传递配置,配置仅会在子组件实例化时解析一次:
第一步:定义注入令牌
// scroller.config.ts import { InjectionToken } from '@angular/core'; export interface ScrollerConfig { aspect: string; res: string; sources: Promise<string[]>; } export const SCROLLER_CONFIG = new InjectionToken<ScrollerConfig>('SCROLLER_CONFIG');
第二步:改造Scroller组件,注入配置
// scroller.component.ts import { Component, Inject, OnInit } from '@angular/core'; import { SCROLLER_CONFIG, ScrollerConfig } from './scroller.config'; @Component({ selector: 'app-scroller', template: `<!-- 原有组件模板 -->` }) export class ScrollerComponent implements OnInit { public movies: string[] = []; constructor(@Inject(SCROLLER_CONFIG) private config: ScrollerConfig) {} ngOnInit() { this.config.sources.then(images => this.movies = images); } }
第三步:父组件用ng-container包裹子组件,提供对应配置
<!-- 父组件模板 --> <ng-container *ngTemplateOutlet="scrollerTpl; context: {$implicit: getImages('cats')}"> <ng-template #scrollerTpl let-config> <app-scroller [providers]="[{provide: SCROLLER_CONFIG, useValue: config}]"></app-scroller> </ng-template> </ng-container> <ng-container *ngTemplateOutlet="scrollerTpl; context: {$implicit: getImages('dogs')}"> <ng-template #scrollerTpl let-config> <app-scroller [providers]="[{provide: SCROLLER_CONFIG, useValue: config}]"></app-scroller> </ng-template> </ng-container>
父组件的getImages方法仅会在模板渲染时执行一次,不会随变更检测重复调用。
内容的提问来源于stack exchange,提问作者PiwiTheKiwi
相关产品推荐
相关产品推荐

