Angular 8中无需EventEmitter,点击按钮后更新兄弟组件的生命周期钩子
解决Angular兄弟组件通过Service共享数据未同步的问题
首先,咱们得明确问题根源:你现在的实现里,展示组件的ingredients只在ngOnInit时赋值一次,之后Service里的数组更新了,但组件不知道这个变化——因为ngOnInit只会在组件初始化时执行一次,后续的按钮点击不会触发它。
而你不想用EventEmitter的话,**最符合Angular最佳实践的方案是用RxJS的可观察对象(Subject/BehaviorSubject)**来实现数据的响应式同步,完全不需要依赖生命周期钩子。下面是具体的改造步骤:
1. 改造ShoppingListService,添加Subject来发布数据更新
在你的Service里引入RxJS的Subject,用来通知所有订阅者食材数组的变化:
import { Injectable } from '@angular/core'; import { Ingredient } from './ingredient.model'; import { Subject } from 'rxjs'; @Injectable({providedIn: 'root'}) export class ShoppingListService { // 创建一个Subject,用于发布食材数组的更新事件 ingredientsChanged = new Subject<Ingredient[]>(); // 私有数组,避免外部直接修改 private ingredients: Ingredient[] = []; getIngredients() { // 返回数组的副本,防止外部直接修改原数组的引用 return [...this.ingredients]; } addIngredient(ingredient: Ingredient) { this.ingredients.push(ingredient); // 发布最新的数组副本,通知所有订阅者 this.ingredientsChanged.next([...this.ingredients]); } }
2. 改造列表展示组件,订阅数据更新
在ShoppinglistComponent里订阅Service的ingredientsChanged Subject,这样每次数组变化时都会自动更新组件的ingredients:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Ingredient } from '../ingredient.model'; import { ShoppingListService } from '../shopping-list.service'; import { Subscription } from 'rxjs'; export class ShoppinglistComponent implements OnInit, OnDestroy { ingredients: Ingredient[]; // 保存订阅对象,用于组件销毁时取消订阅,防止内存泄漏 private ingredientSub: Subscription; constructor(private shoppingListService: ShoppingListService) { } ngOnInit() { // 初始化时获取当前的食材数组 this.ingredients = this.shoppingListService.getIngredients(); // 订阅数据更新事件 this.ingredientSub = this.shoppingListService.ingredientsChanged.subscribe( (updatedIngredients: Ingredient[]) => { this.ingredients = updatedIngredients; } ); } // 组件销毁时取消订阅 ngOnDestroy() { this.ingredientSub.unsubscribe(); } }
为什么不推荐用生命周期钩子?
如果硬要用生命周期钩子的话,你可以用ngDoCheck——它会在每次Angular变更检测周期运行时触发,你可以在里面检查数组长度是否变化来更新数据:
import { Component, OnInit, DoCheck } from '@angular/core'; import { Ingredient } from '../ingredient.model'; import { ShoppingListService } from '../shopping-list.service'; export class ShoppinglistComponent implements OnInit, DoCheck { ingredients: Ingredient[]; private prevLength: number; constructor(private shoppingListService: ShoppingListService) { } ngOnInit() { this.ingredients = this.shoppingListService.getIngredients(); this.prevLength = this.ingredients.length; } ngDoCheck() { const currentIngredients = this.shoppingListService.getIngredients(); if (currentIngredients.length !== this.prevLength) { this.ingredients = currentIngredients; this.prevLength = currentIngredients.length; } } }
但这种方式的问题很明显:ngDoCheck会非常频繁地执行(每次页面有任何变化都会触发),会带来不必要的性能开销,所以强烈推荐用RxJS的Subject方案,这也是Angular中跨组件共享状态的标准做法。
内容的提问来源于stack exchange,提问作者Brian
相关产品推荐
相关产品推荐

