即使在组件销毁后订阅服务 observable 也会发出

Subscribtion to service observable emits even after component destruction

我在使用服务中的可观察对象时遇到问题。以下代码说明了这一点:

@Injectable({
  providedIn: 'root'
})
export class MyService {
  public globalVariable: BehaviorSubject<string> = new BehaviorSubject('');
}

我有一个功能组件:

export class ComponentA implements OnInit {
   constructor(public myService : MyService ) {
      this.myService.globalVariable.next('newValue');
   }

   ngOnInit() {
      this.myService.globalVariable.subscribe(_ => console.log('=> hello'));
   }
}

应用程序模块:

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    ComponentAModule,
    ComponentBModule,
    AppRoutingModule
  ],
  providers: [MyService],
  bootstrap: [AppComponent]
})
export class AppModule {
}

最后是项目结构:

app-module.ts
app-routing.module.ts
-components
-- componentA
--- componentA.module.ts
--- componentA-routing.module.ts
--- componentA.component.ts
--- componentA.component.html
-- componentB
--- componentB.module.ts
--- componentB-routing.module.ts
--- componentB.component.ts
--- componentB.component.html

现在我面临的问题是,当我导航到 componentA 时,输出是:

=> hello
=> hello

到现在为止一切正常,表现如我所料。第一个订阅被触发,然后 componentA 的构造函数 globalVariable 中的变化被触发。

但是,当我导航到 componentB 并导航回 componentA 时,输出是:

=> hello
=> hello
=> hello

每次我导航回 componentA 它都会加一个。好像它创建了一个新的 MyService 实例?或者离开时不销毁订阅?

信息:没有延迟加载。

您需要 unsubscribengOnDestroy 内:

import { Subscription } from 'rxjs';

globalVariable$: Subscription;

ngOnInit() {
  this.globalVariable$ = this.myService.globalVariable.subscribe(_ => console.log('=> hello'));
}

ngOnDestroy() {
  this.globalVariable$.unsubscribe();
}

如果订阅不是由 Angular 自己处理,则必须手动销毁。这基本上适用于您拥有的所有 httpClient 订阅。如果你例如使用 | async 管道,Angular 负责取消订阅。

在组件的 ngOnDestroy() 中调用 yourSubscription.unsubscribe()

我通常做的是创建一个 BaseComponent 来为我取消订阅。通过扩展在所有组件中使用下面的 class。将每个订阅调用包装在 super.addSubscription()

import { OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

/**
 * This class handles the subscribing and unsubscribing of subscriptions to avoid memory leaks
 * and can be inherited by members
 *
 * @export
 */
export abstract class BaseComponent implements OnDestroy {

private subscriptions: Subscription[] = new Array<Subscription>();

ngOnDestroy() {
    this.removeSubscriptions();
}

/**
 * Adds a subscriptions so it can be deleted in ngOnDestroy
 *
 * @param subscription The subscription that should be added
 * @memberof BaseComponent
 */
protected addSubscription(subscription: Subscription) {
    this.subscriptions.push(subscription);
}

/**
 * Unsubscribes from any open subscriptions in the subscriptions array in ngOnDestroy
 *
 * @memberof AbstractBaseComponent
 */
private removeSubscriptions() {
    for (let subscription of this.subscriptions) {
        subscription.unsubscribe();
    }
}
}

更新

为您的 ngOnInit() 执行以下操作,假设您使用上面提供的基础 class:

export class ComponentA extends BaseComponent implements OnInit {
    constructor(public myService : MyService ) {
       this.myService.globalVariable.next('newValue');
    }
    ngOnInit() {
       super.addSubscription(
           this.myService.globalVariable.subscribe(_ => console.log('=> hello'))
       )
    }
}

如果你想使用订阅而不是异步管道,你可以使用 RxJs 运算符 takeWhile。请看下面的代码...

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs';
import { takeWhile, map } from 'rxjs/operators';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit, OnDestroy {
  name = 'Angular';
  isActive: boolean;
  // returned from service.  Does not need to initialized here
  thingToSubscribeTo:Observable<any> = new Observable<any>();

  ngOnInit() {
    this.isActive = true;
    // you can replace with call to service then pipe it.
    this.thingToSubscribeTo.pipe(
      map(res => {
        // handle subscription
      }),
      takeWhile(() => this.isActive)
    );

  }

  ngOnDestroy() {
    this.isActive = false;
  }
}