什么时候我应该存储订阅实例和调用unsubscribe()在ngOnDestroy生命周期,什么时候我可以简单地忽略它们?

保存所有订阅会给组件代码带来很多麻烦。

HTTP客户端指南忽略这样的订阅:

getHeroes() {
  this.heroService.getHeroes()
                  .subscribe(
                     heroes => this.heroes = heroes,
                     error =>  this.errorMessage = <any>error);
}

同时,《航路指南》指出:

最终,我们会航行到别的地方。路由器将从DOM中移除这个组件并销毁它。在那之前,我们得把自己弄干净。具体来说,我们必须在Angular销毁该组件之前取消订阅。如果不这样做,可能会产生内存泄漏。 我们在ngOnDestroy方法中取消订阅我们的可观察对象。

private sub: any;

ngOnInit() {
  this.sub = this.route.params.subscribe(params => {
     let id = +params['id']; // (+) converts string 'id' to a number
     this.service.getHero(id).then(hero => this.hero = hero);
   });
}

ngOnDestroy() {
  this.sub.unsubscribe();
}

当前回答

对于每一个订阅,你都需要取消订阅。Advantage =>以防止状态变得过于沉重。

例如: 在组件1中:

import {UserService} from './user.service';

private user = {name: 'test', id: 1}

constructor(public userService: UserService) {
    this.userService.onUserChange.next(this.user);
}

在服务:

import {BehaviorSubject} from 'rxjs/BehaviorSubject';

public onUserChange: BehaviorSubject<any> = new BehaviorSubject({});

于component2:

import {Subscription} from 'rxjs/Subscription';
import {UserService} from './user.service';

private onUserChange: Subscription;

constructor(public userService: UserService) {
    this.onUserChange = this.userService.onUserChange.subscribe(user => {
        console.log(user);
    });
}

public ngOnDestroy(): void {
    // note: Here you have to be sure to unsubscribe to the subscribe item!
    this.onUserChange.unsubscribe();
}

其他回答

SubSink包,一个简单而一致的取消订阅的解决方案

由于没有人提到它,我想推荐Ward Bell创建的Subsink包:https://github.com/wardbell/subsink#readme。

我一直在一个项目中使用它,我们有几个开发人员都在使用它。在任何情况下都有一种一致的工作方式是很有帮助的。

出于性能原因,总是建议从你的可观察订阅中取消订阅,以避免内存泄漏,有不同的方法,

顺便说一下,我读了大部分的答案,我没有发现有人在谈论异步管道,推荐在Angular应用中使用Rxjs模式,因为它在离开组件时会自动提供订阅和订阅,而这些订阅将被销毁:

请找到一个如何实现它的例子

app.compoennt.ts:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

import { BookService } from './book.service';
import { Book } from './book';

@Component({
   selector: 'app-observable',
   templateUrl: './observable.component.html'
})
export class AppComponent implements OnInit { 
   books$: Observable<Book[]>
   constructor(private bookService: BookService) { }
   ngOnInit(): void {
        this.books$ = this.bookService.getBooksWithObservable();
   }
} 

app.compoennt.html:

<h3>AsyncPipe with Promise using NgFor</h3>
<ul>
  <li *ngFor="let book of books$ | async" >
    Id: {{book?.id}}, Name: {{book?.name}}
  </li>
</ul>

由于seangwright的解决方案(编辑3)似乎非常有用,我也发现将这个功能打包到基本组件中是一个痛苦的过程,并提示其他项目队友记住在ngOnDestroy上调用super()来激活这个功能。

这个答案提供了一种从super调用中释放的方法,并使"componentDestroyed$"成为base component的核心。

class BaseClass {
    protected componentDestroyed$: Subject<void> = new Subject<void>();
    constructor() {

        /// wrap the ngOnDestroy to be an Observable. and set free from calling super() on ngOnDestroy.
        let _$ = this.ngOnDestroy;
        this.ngOnDestroy = () => {
            this.componentDestroyed$.next();
            this.componentDestroyed$.complete();
            _$();
        }
    }

    /// placeholder of ngOnDestroy. no need to do super() call of extended class.
    ngOnDestroy() {}
}

然后你可以自由地使用这个功能,例如:

@Component({
    selector: 'my-thing',
    templateUrl: './my-thing.component.html'
})
export class MyThingComponent extends BaseClass implements OnInit, OnDestroy {
    constructor(
        private myThingService: MyThingService,
    ) { super(); }

    ngOnInit() {
        this.myThingService.getThings()
            .takeUntil(this.componentDestroyed$)
            .subscribe(things => console.log(things));
    }

    /// optional. not a requirement to implement OnDestroy
    ngOnDestroy() {
        console.log('everything works as intended with or without super call');
    }

}

下面是我对这个问题的看法,保持我的生活简单,我选择手动方式取消订阅时,组件被破坏。

为此,我创建了一个名为Subscriptor的类,它主要包含静态成员,即:

私有变量subscriptions——它保存所有提供的订阅 订阅设置器——将每个新订阅推送到订阅数组 一个取消订阅方法——如果定义了订阅数组,则取消订阅数组中包含的每个订阅,并清空订阅数组

subscriptor.ts

import { Subscription } from "rxjs";

export class Subscriptor {
    private static subscriptions: Subscription[] = [];

    static set subscription(subscription: Subscription) {
        Subscriptor.subscriptions.push(subscription);
    }

    static unsubscribe() {
        Subscriptor.subscriptions.forEach(subscription => subscription ? subscription.unsubscribe() : 0);
        Subscriptor.subscriptions = [];
    }
}

组件内部的用法如下:

当您想订阅任何服务时,只需将订阅放到Subscriptor的setter中即可。

ngOnInit(): void {
    Subscriptor.subscription = this.userService.getAll().subscribe(users => this.users = users);
    Subscriptor.subscription = this.categoryService.getAll().subscribe(categories => this.categories = categories);
    Subscriptor.subscription = this.postService.getAll().subscribe(posts => this.posts = posts);
}

当您想取消订阅任何服务时,只需调用Subscriptor的unsubscribe方法。

ngOnDestroy(): void {
    Subscriptor.unsubscribe();
}

当组件被销毁时,你通常需要取消订阅,但随着我们的发展,Angular会越来越多地处理它,例如在Angular4的新小版本中,他们有这个部分用于路由取消订阅:

你需要取消订阅吗?如在 ActivatedRoute:一站式的路线信息部分 路由和导航页面,路由器管理它的观察对象 提供并本地化订阅。订阅是 当组件被破坏时进行清理,以防止内存 泄漏,所以你不需要从路由paramMap取消订阅 可观察到的。

下面的例子也是Angular中创建一个组件并销毁它的好例子,看看组件是如何实现OnDestroy的,如果你需要onInit,你也可以在你的组件中实现它,比如实现onInit, OnDestroy

import { Component, Input, OnDestroy } from '@angular/core';  
import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs/Subscription';

@Component({
  selector: 'my-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()"
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})

export class AstronautComponent implements OnDestroy {
  @Input() astronaut: string;
  mission = '<no mission announced>';
  confirmed = false;
  announced = false;
  subscription: Subscription;

  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
  }

  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }

  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }
}