为何Angular组件销毁后Subscription仍存活?技术咨询
为什么Angular组件销毁后对应的Subscription仍会存活?
先看你的组件代码(补全了订阅里的控制台输出语句):
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/interval'; @Component({ selector: 'app-my', templateUrl: './my.component.html', styleUrls: ['./my.component.scss'] }) export class MyComponent implements OnInit { a = 'I still alive'; constructor() {} ngOnInit() { Observable.interval(1000).subscribe(x => console.log(x)); } }
这个问题我碰到过好多次!核心原因很简单:Angular组件销毁时,不会自动清理你手动创建的RxJS订阅。你这里直接调用了subscribe(),但既没有保存订阅返回的Subscription实例,也没有在组件销毁时执行取消订阅的操作,所以这个interval会一直每隔1秒发射一次值,哪怕组件已经被从DOM中移除、不再被使用,最终会导致内存泄漏。
给你几种常用的解决方法,按需选择:
方法1:手动保存Subscription,在ngOnDestroy中取消订阅
这是最直观的方式,适合单一订阅的场景:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Observable, Subscription } from 'rxjs/Observable'; import 'rxjs/add/observable/interval'; @Component({ selector: 'app-my', templateUrl: './my.component.html', styleUrls: ['./my.component.scss'] }) export class MyComponent implements OnInit, OnDestroy { a = 'I still alive'; private intervalSubscription: Subscription; // 保存订阅实例 constructor() {} ngOnInit() { // 把订阅结果存到变量里 this.intervalSubscription = Observable.interval(1000).subscribe(x => { console.log(x); }); } ngOnDestroy() { // 组件销毁时取消订阅 if (this.intervalSubscription) { this.intervalSubscription.unsubscribe(); } } }
方法2:使用Angular的async管道(推荐给模板绑定场景)
async管道会自动帮你管理订阅的创建和销毁,不用写额外的取消逻辑,非常省心:
组件类代码
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/interval'; @Component({ selector: 'app-my', templateUrl: './my.component.html', styleUrls: ['./my.component.scss'] }) export class MyComponent implements OnInit { a = 'I still alive'; interval$: Observable<number>; // 把Observable暴露给模板 constructor() {} ngOnInit() { this.interval$ = Observable.interval(1000); } }
模板代码(my.component.html)
<!-- 用async管道处理订阅 --> <p>当前计数:{{ interval$ | async }}</p>
方法3:使用takeUntil操作符(适合多订阅场景)
如果组件里有多个订阅,一个个保存Subscription会很繁琐,用takeUntil配合一个销毁信号Subject是更优雅的方式:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Observable, Subject } from 'rxjs/Observable'; import 'rxjs/add/observable/interval'; import 'rxjs/add/operator/takeUntil'; @Component({ selector: 'app-my', templateUrl: './my.component.html', styleUrls: ['./my.component.scss'] }) export class MyComponent implements OnInit, OnDestroy { a = 'I still alive'; private destroy$ = new Subject<void>(); // 销毁信号Subject constructor() {} ngOnInit() { Observable.interval(1000) .takeUntil(this.destroy$) // 直到destroy$发射值,自动停止订阅 .subscribe(x => { console.log(x); }); // 如果有其他订阅,也可以用同样的方式处理 // someOtherObservable$.takeUntil(this.destroy$).subscribe(...); } ngOnDestroy() { this.destroy$.next(); // 发射销毁信号 this.destroy$.complete(); // 完成Subject,避免内存泄漏 } }
内容的提问来源于stack exchange,提问作者siva636
相关产品推荐
相关产品推荐

