原文/出处: RxJS & Angular — Unsubscribe Like a Pro

在angular项目中我们不可避免的要使用RxJS可观察对象(Observables)来进行订阅(Subscribe)和退订(Unsubscribe)操作;

概述

我们的每个angular项目中都会用到RxJS, RxJS在我们的angular app中对数据流和性能有非常大的影响。

为了避免内存泄漏,在适当的时机对可观察对象进行退订是非常重要的; 本文会向你展示各种在angular组件中退订可观察对象的方法!

首先,我们创建一个帮助类来帮我们创建的订阅对象(Subscription)


@Injectable({
providedIn: 'root',
})
export class DummyService {
getEmissions(scope: string): Observable<string> {
return Observable.create((observer) => {
console.log(`${scope} Subscribed`); const subscription: Subscription = timer(0, 1000)
.pipe(
map((n) => `${scope} Emission #${n}`),
tap(console.log)
)
.subscribe(observer); return () => {
subscription.unsubscribe();
console.log(`${scope} Unsubscribed`);
};
});
}
}

我们的帮助类有一个getEmissions方法, 它接受一个scope参数来记录日志, 它的返回值是一个会每秒发出 ${scope} Emission #n字符串的可观察对象.

方式一 "常规"的取消订阅的方式

最简单的订阅和取消订阅一个可观察对象的方式是在 ngOnInit 方法中订阅可观察对象(Observable), 然后在组件类中创建一个类属性用来保存这个订阅(Subscription), 并在 ngOnDestroy 中取消对可观察对象对订阅. 简单起见, 我们可以使用Subscription.EMPTY来初始化一个订阅对象(Subscription), 这样可以防止在取消订阅时遇到空引用对问题.

@Component({
selector: 'app-regular',
template: `<div>{{ emission }}</div>`,
})
export class RegularComponent implements OnInit, OnDestroy {
emission: string; /*
Note: we initialize to Subscription.EMPTY to avoid null checking within ngOnDestroy
*/
private subscription: Subscription = Subscription.EMPTY; constructor(private dummyService: DummyService) {} ngOnInit(): void {
this.subscription = this.dummyService
.getEmissions('Regular')
.subscribe((emission) => (this.emission = emission));
} ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}

为了验证代码有效我们在三秒后从DOM中移除这个组件

如上所述, 这是最基本对取消订阅的方式, 如果我们的组件类中只有一个订阅对象(Subscription), 这种方式没什么问题. 但是当我们有多个订阅对象(Subscription)时, 针对每一个我们都需要在组件类中创建一个字段保存这个对象的的引用并在 ngOnDestroy 中调用 unsubscribe来取消订阅.

方式二 使用 Subscription.add 方法

RxJS的订阅类(Subscription)内建了 Subscription.add 方法允许我们使用单个订阅对象的实例(Subscription instance)来简化我们操作多个订阅对象的.

首先, 在组件类中使用new Subscription()实例化创建一个字段, 然后调用该实例的 Subscription.add 方法, 最后在 ngOnDestroy 中取消订阅.

@Component({
selector: 'app-add',
template: `
<div>{{ emissionA }}</div>
<div>{{ emissionB }}</div>
`,
})
export class AddComponent implements OnInit, OnDestroy {
emissionA: string;
emissionB: string; private subscription: Subscription = new Subscription(); constructor(private dummyService: DummyService) {} ngOnInit(): void {
this.subscription.add(
this.dummyService
.getEmissions('[Add A]')
.subscribe((emission) => (this.emissionA = emission))
); this.subscription.add(
this.dummyService
.getEmissions('[Add B]')
.subscribe((emission) => (this.emissionB = emission))
);
} ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}

打开浏览器控制台, 我们可以看到两个订阅对象:

使用这种方式, 我们可以使用RsJS内建的方法轻松的取消订阅多个可观察对象而不必在组件类创建多个字段保存订阅对象的引用.

方式三 AsyncPipe

Angular内置了许多非常有用的管道(pipe), 其中一个就是AsyncPipe. AsyncPipe接受一个可观察对象并在组件生命周期结束时(ngOnDestroy)自动取消订阅.

与前两个示例不同, 这里我们不需要在组件中手动取消订阅, 而是将可观察对象(Observable)传递个 AsyncPipe:

@Component({
selector: 'app-async',
template: `<div>{{ emissions$ | async }}</div>`
})
export class AsyncComponent implements OnInit {
emissions$: Observable<string>; constructor(private dummyService: DummyService) {} ngOnInit(): void {
this.emissions$ = this.dummyService.getEmissions('[Async]');
}
}

在我看来, 这是在Angular中使用可观察对象(Observables)最简明的方式. 你只需创建可观察对象(Observables)然后Angular会帮助你进行订阅和取消订阅.

方式4 takeUntil 操作符

RxJS包含许多有用的操作符, takeUntil就是其中之一. 像这个操作符的签名一样, takeUntil 接受一个会发出取消订阅源可观察对象通知的可观察对象(notifier).

在我们的示例中, 我们希望在组件被销毁后发出通知, 所以我们给组件类添加一个叫 componentDestroyed$ 的字段, 它的类型是 Subject<void>, 这个字段承担了通知人(notifier)的角色.

然后我们只需在ngOnDestroy发出"通知"即可, 最终的代码像下面这样:

@Component({
selector: 'app-until',
template: `<div>{{ emission }}</div>`,
})
export class UntilComponent implements OnInit, OnDestroy {
emission: string; private componentDestroyed$: Subject<void> = new Subject<void>(); constructor(private dummyService: DummyService) {} ngOnInit(): void {
this.dummyService
.getEmissions('takeUntil')
.pipe(takeUntil(this.componentDestroyed$))
.subscribe((emission) => (this.emission = emission));
} ngOnDestroy(): void {
this.componentDestroyed$.next();
}
}

与之前常规的方式相比, 这种方式在我们有多个订阅对象时不必在组件类中创建多个字段保存对订阅对象的引用. 我们只需在管道中加入 takeUntil(componentDestroyed$) 即可, 剩下的RxJS会帮我们完成.

方式五 SubSink 库

SubSinkWard Bell写的一个很棒的库, 它使你可以优雅的在你的组件中取消对可观察对象的订阅.

首先, 通过npm i subsinkyarn add subsink安装SubSink. 然后在组件类中创建一个SubSink类型的字段.

SubSink有两种方式, 一种是简单技术(使用sink属性设置器), 另一种是 数组/添加(Array/Add)技术.

使用简单技术只需要使用sink设置器属性即可. 使用_数组/添加(Array/Add)技术_的话代码类似RxJS原生的Subscription.add

为每一种方式创建一个订阅对象, 我们的组件类看起来像下面这样

@Component({
selector: 'app-sink',
template: `
<div>{{ easyEmission }}</div>
<div>{{ arrayAddEmission }}</div>
`,
})
export class SinkComponent implements OnInit, OnDestroy {
easyEmission: string;
arrayAddEmission: string; private subs = new SubSink(); constructor(private dummyService: DummyService) {} ngOnInit(): void {
/* 使用简单技术 */
this.subs.sink = this.dummyService
.getEmissions('[Easy Technique]')
.subscribe((emission) => (this.easyEmission = emission)); /* 使用数组/添加(Array/Add)技术 */
this.subs.add(
this.dummyService
.getEmissions('[Array/Add Technique]')
.subscribe((emission) => (this.easyEmission = emission))
);
} ngOnDestroy(): void {
this.subs.unsubscribe();
}
}

方式六 until-destroy 库

注意: 这个库在Pre Ivy Angular上行为不同, 更多信息请访问文档

until-destroyngneat许多很棒的库之一, 它使用UntilDestroy装饰器来确认哪些字段的是订阅对象(Subscriptions)并在组件销毁时取消订阅它们;

我们还可以不通过组件类字段, 而是使用_until-destroy_定义的叫untilDestroyed的RxJS操作符来取消订阅.

要使用它我们需要给组件类加上 UntilDestroy 装饰器, 然后在可观察对象管道中加入 untilDestroyed 操作符:

@UntilDestroy()
@Component({
selector: 'app-destroyed',
template: `<div>{{ emission }}</div> `,
})
export class DestroyedComponent implements OnInit {
emission: string; constructor(private dummyService: DummyService) {} ngOnInit(): void {
this.dummyService
.getEmissions('[UntilDestroy]')
.pipe(untilDestroyed(this))
.subscribe((emission) => (this.emission = emission));
}
}

总的来说, until-destroy是个非常强大的库, 他可以帮你自动取消对可观察对象的订阅.

此外, until-destroy还有许多其他在本文中没有进行说明的特性, 所以赶快去看看它们的github仓库吧!

总结

上面我们已经看到来许多订阅和退订可观察对象方式, 每个都各有各的优劣并且有着不同的编码风格.

但是最重要是不管我们选择那种方式, 我们都要保持编码风格的一致

[译]Rxjs&Angular-退订可观察对象的n中方式的更多相关文章

  1. Angular的Observable可观察对象(转)

    原文:https://blog.csdn.net/qq_34414916/article/details/85194098 Observable 在开始讲服务之前,我们先来看一下一个新东西——Obse ...

  2. EDM备忘录:触发式邮件订阅和退订功能介绍

    一般来说,有触发式邮件订阅和退订功能是邮件模板设计中必不可少的两项功能,下面博主为大家介绍一下. 若客户在订阅后不想再收到这类邮件即可选择退订,将不再收到该IP地址的推广邮件,避免客户在继续收到这类邮 ...

  3. RxJS 简介:可观察对象、观察者与操作符

    RxJS 简介:可观察对象.观察者与操作符 对于响应式编程来说,RxJS 是一个不可思议的工具.今天我们将深入探讨什么是 Observable(可观察对象)和 observer(观察者),然后了解如何 ...

  4. ngx通讯之可观察对象实现

    1.公共服务 //test.service.ts import {Injectable} from '@angular/core'; import {Subject} from 'rxjs/Subje ...

  5. [译]RxJS 5.X基础篇

    欢迎指错与讨论 : ) 当前RxJS版本:5.0.0-beta.10.更详细的内容尽在RxJS官网http://reactivex.io/rxjs/manual/overview.html.文章比较长 ...

  6. [译] 关于 Angular 依赖注入你需要知道的

    如果你之前没有深入了解 Angular 依赖注入系统,那你现在可能认为 Angular 程序内的根注入器包含所有合并的服务提供商,每一个组件都有它自己的注入器,延迟加载模块有它自己的注入器. 但是,仅 ...

  7. wex5 教程 之 图文讲解 可观察对象的集群应用与绑定技术

    一 前言: wex5官方教程里,开篇即以一个input输入,output即时输出的例子,为我们展现了一个概念:可观察对象.在以后我的项目开发中,将大量运用可观察对象. 那么,问题来了: 1. 可观察对 ...

  8. Angular2 Observable 可观察对象

    可观察对象支持在应用中的发布者和订阅者之间传递消息.在需要进行事件处理,异步编程和处理多值的时候,可观察对象相对其他技术有显著的优点. 可观察对象是声明式的 —— 也就是说,虽然你定义了一个用于发布值 ...

  9. RxJS & Angular

    RxJS & Angular https://www.learnrxjs.io/ https://rxjs-cn.github.io/learn-rxjs-operators/ https:/ ...

随机推荐

  1. Golang字符串是否存在于切片或数组中的小工具(基本等同于python in语法)

    // golang中是没有判断字符串是否存在数组或切片里的内置方法, 所以我们可以自己搞一个快速小组件 func Find(slice []string, val string) (int, bool ...

  2. 结合MATLAB、Python、R语言,在求得显著差异的边(节点对)之后,怎么画circle图

                                                            先来看看成果图: OK,开始画图: 实验背景声明:在脑影像分析中,我们首先构建脑网络,然 ...

  3. Java进阶专题(二十一) 消息中间件架构体系(3)-- Kafka研究

    前言 Kafka 是一款分布式消息发布和订阅系统,具有高性能.高吞吐量的特点而被广泛应用与大数据传输场景.它是由 LinkedIn 公司开发,使用 Scala 语言编写,之后成为 Apache 基金会 ...

  4. 【MySQL】汇总数据 - avg()、count()、max()、min()、sum()函数的使用

    第12章 汇总数据 文章目录 第12章 汇总数据 1.聚集函数 1.1.AVG()函数 avg() 1.2.COUNT()函数 count() 1.3. MAX()函数 max() 1.4.MIN() ...

  5. linux下删除文件夹及下面所有文件

    使用rm -rf 目录名字 命令即可 -r 就是向下递归,不管有多少级目录,一并删除-f 就是直接强行删除,不作任何提示的意思 rm 不带参数 只能删除文件 rm test.txt mkdir /us ...

  6. 好你个C语言,原来还有这么多副面孔!

    C语言可以这样比喻,是一门非常强大的内功心法,学会它可以做到一法通万法.这也是它至今不衰的原因.说了这么多C语言的优点,现在来说说它的缺点.C语言最大的优点也是它最大的缺点,拥有强大的力量时应时刻保持 ...

  7. Django中多表关联的展示问题:

    增加一个知识点,当表中有多对多关联时,在前端展示的时候是一个列表,所以在展示的时候需要这样做: 表结构: class ProjectEnv(models.Model): project = model ...

  8. Py第一次练习,第二次练习

    有两个列表      l1 = [11,22,33]        l2 = [22,33,44] 1.获取内容相同的元素列表2.获取l1中有l2中没有的元素列表3.获取l1和l2中不同的元素列表 l ...

  9. (013)每日SQL学习:日期的各种计算

    1.确定两个日期之间的工作日天数 --确定两个日期之间的工作日天数with x0 as (select to_date('2018-01-01','yyyy-mm-dd') as 日期 from du ...

  10. 加密填补 填充 pad padding

    RFC 1423 - Privacy Enhancement for Internet Electronic Mail: Part III: Algorithms, Modes, and Identi ...