即使在组件销毁后订阅服务 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
实例?或者离开时不销毁订阅?
信息:没有延迟加载。
您需要 unsubscribe
在 ngOnDestroy
内:
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;
}
}
我在使用服务中的可观察对象时遇到问题。以下代码说明了这一点:
@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
实例?或者离开时不销毁订阅?
信息:没有延迟加载。
您需要 unsubscribe
在 ngOnDestroy
内:
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;
}
}