组件销毁并重新访问后,订阅在 ngOnInit 函数中运行

Posted

技术标签:

【中文标题】组件销毁并重新访问后,订阅在 ngOnInit 函数中运行【英文标题】:Subscription runs in ngOnInit function after Component is destroyed and Revisited 【发布时间】:2020-11-15 09:23:56 【问题描述】:

我在使用 Angular 组件和 rxjs 订阅时遇到了一个非常奇怪的情况。

我有以下ngOnInitngOnDestroy 函数

ngOnInit() 
    zip(this.service.getMessage, this.service.getType)
      .pipe(takeUntil(this.componentDestroyed$))
      .subscribe(data => 
        const notification = new Notification(data[0], data[1]);
        this.notifications.push(notification);
    );


ngOnDestroy(): void 
    this.componentDestroyed$.next(true);
    this.componentDestroyed$.complete();

在服务文件中使用 Source 和 Subject 范例设置值后,订阅处于活动状态,如下所示:

private messageSource = new BehaviorSubject<string>('');
private message = this.messageSource.asObservable();
private typeSource = new BehaviorSubject<number>(-1);
private type = this.typeSource.asObservable();

...
...

set setMessage(message: string) 
  this.messageSource.next(message);


set setType(type: number) 
  this.typeSource.next(type);

如预期的那样,初始订阅工作正常。但是,离开组件并导航回同一个组件会在ngOnInit 中再次运行zip 订阅,即使在组件在离开时被销毁之后也是如此。如何防止这种情况发生?我还尝试定义订阅变量并调用unsubscribe。我被难住了。

【问题讨论】:

当您导航回组件时,组件会从头开始初始化,ngOnInit再次运行。所以很自然地会再次发起订阅。 嘿@MichaelD,谢谢。没错。但是,ngOnInit 中的订阅包含在组件被销毁之前发出的先前值。如果我在返回时没有发出任何值,这些值不应该为空吗? BehaviorSubject 保存当前值(即使它在组件被销毁之前发出)并在订阅时立即发出。如果组件被创建或销毁,服务中的BehaviorSubjects 没有上下文,它们在订阅后立即编辑。如果您希望 observables 仅在 推送一个新值之后发出,您可以使用 Subject 而不是 BehaviorSubject @MichaelD 我花了几个小时跟踪这个,BehaviorSubject 一直为我的案例工作,除了这次。我一直使用它,但从来不知道BehaviorSubjectSubject 之间的细微差别。谢谢!如果您花时间发布,我会接受您的解决方案。 不客气 :)。我已经发布了答案。 【参考方案1】:

为了扩展我的评论,RxJS BehaviorSubjects 保存推送给它的当前值并在订阅时立即发出它。它还允许您在BehaviorSubject 上使用唯一的getValue() 函数(或value getter),该函数允许您同步检索它所持有的当前值。虽然这个 getter 的用法通常是不受欢迎的。

因此,在您的情况下,当您路由回组件时,订阅会发出两个可观察对象先前持有的值。相反,您可以使用 RxJS Subject,它 not 保存值并仅在将值推送到它之后才发出。

private messageSource = new Subject<string>();
private message = this.messageSource.asObservable();
private typeSource = new Subject<number>();
private type = this.typeSource.asObservable();
...

我还建议您查看 RxJS ReplaySubject。这是一个更灵活的多播可观察对象。它接收要缓冲的通知数量,并在新订阅时立即发出它们。

所以ReplaySubject(1)(缓冲区1)类似于BehaviorSubject,只是它不需要默认值。因此,如果您在尚未推送任何内容时订阅它,它不会发出。

它对您当前的问题没有帮助,但在您希望拥有BehaviorSubject 的行为但不希望处理不必要的默认值时可能会有所帮助。

【讨论】:

以上是关于组件销毁并重新访问后,订阅在 ngOnInit 函数中运行的主要内容,如果未能解决你的问题,请参考以下文章

Angular 组件在被销毁后仍在监听订阅 [重复]

组件 ngOnInit 中的 Angular 订阅

为啥组件销毁后订阅仍然存在?

删除在 ngOnInit() 上订阅服务创建的空行

Angular Jasmine 测试未在 ngOnInit 中触发订阅

在哪里订阅 Angular 中的 observable、构造函数或 ngoninit [关闭]