防止Angular 2中的内存泄漏?

22
在Angular 2中,我应该注意哪些与内存管理相关的特定陷阱?
为了避免可能的泄漏,如何管理组件状态的最佳实践是什么?
具体来说,我看到有人在ngOnDestroy方法中取消HTTP observables。我是否应该总是这样做?
在Angular 2组件中,观察者会怎样呢?就像在Angular 1.X中,当$scope被销毁时,所有监听器也会自动销毁一样吗?
@Component({
  selector: 'library',
  template: `
    <tr *ngFor="#book of books | async">
        <td>{{ book.title.text }}</td>
        <td>{{ book.author.text }}</td>
    </tr>
  `
})
export class Library {
    books: Observable<any>;

    constructor(private backend: Backend) {
        this.books = this.backend.get('/texts'); // <-- does it get destroyed
                                                 //     with the component?
    }
};

不要忘记在 ngOnDestroy 中取消订阅 Observable。 - Mourad Zouabi
2
在您的情况下,在 ngOnDestroy 中取消订阅 Observable 是不必要的,因为 Async 管道已经为您处理了所有内容。这将是一种好的实践,与其自己订阅,不如让管道为您完成所有操作。 - Eric Martinez
@EricMartinez,谢谢!如果您提供一个证明链接,我会接受它作为答案。 - katspaugh
这回答解决了您的问题吗?需要取消订阅由Http方法创建的Observables吗? - Liam
2个回答

16

按照 @katspaugh 的要求

在您的特定情况下,没有必要手动取消订阅,因为这是 Async pipe 的工作。

请检查 AsyncPipe 的源代码。出于简洁起见,我只发布相关的代码

class AsyncPipe implements PipeTransform, OnDestroy {
    // ...
    ngOnDestroy(): void {
        if (isPresent(this._subscription)) {
          this._dispose();
        }
    }

正如你所看到的,Async管道实现了OnDestroy接口,当它被销毁时,会检查是否存在某些订阅并将其删除。

在这种特定情况下,你会重复造轮子(很抱歉我再次重申)。这并不意味着你不能/不应该在其他情况下像你引用的那个案例一样手动取消订阅。在那种情况下,用户在组件之间传递Observable以进行通信,因此手动取消订阅是一个好的做法。

我不确定框架是否能够检测到任何“活动”的订阅,并在组件被销毁时自动取消它们,这当然需要进一步的调查。

希望这能对Async管道有所澄清。


嗨,@katspaugh,我从Rob那里得到了答案,我认为你可能会感兴趣,点击这里查看评论 - Eric Martinez
1
请查看这个谈话视频(27:40分钟),虽然它没有深入探讨这个主题,但是因为你的问题我对它很感兴趣,并找到了这个视频。 - Eric Martinez
3
谢谢,很有趣!Rob的意思是说,你不需要取消订阅只产生单个值的可观察对象(比如HTTP请求)的订阅。 - katspaugh

4

您不必取消订阅标准订阅,例如 http.get()。 但是,您必须取消订阅自定义主题上的订阅。如果您有一些组件,并且在其中订阅了服务中的某个主题,则每次显示该组件时,新的订阅将添加到该主题中。

Problems with my service

Please check this out: 如何优雅地让你的组件更加整洁 我的个人做法是,所有的组件都继承自这个优秀的类:

import { OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs/Subject';

/**
 * A component that cleans all subscriptions with oneself
 * https://dev59.com/ZFoT5IYBdhLWcg3w8S-2
 * @class NeatComponent
 */
export abstract class NeatComponent implements OnDestroy, OnInit {
// Add '.takeUntil(this.ngUnsubscribe)' before every '.subscrybe(...)'
// and this subscriptions will be cleaned up on component destroy.

  protected ngUnsubscribe: Subject<any> = new Subject();

  public ngOnDestroy() {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

  public ngOnInit(){}
}

我只是在构造函数中添加了super()调用,并在每个订阅之前加上了.takeUntil(this.ngUnsubscribe)

import { NeatComponent } from '../../types/neat.component';

@Component({
  selector: 'category-selector',
  templateUrl: './category-selector.component.pug'
})
export class CategorySelectorComponent extends NeatComponent {

  public constructor(
    private _shopService: ShopsService
  ) { super(); }

  public ngOnInit() {
    this._shopService.categories.takeUntil(this.ngUnsubscribe)
      .subscribe((categories: any) => {
        // your code here
      })
  }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接