[译]Rxjs&Angular-退订可观察对象的n中方式
在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 库
SubSink是Ward Bell写的一个很棒的库, 它使你可以优雅的在你的组件中取消对可观察对象的订阅.
首先, 通过npm i subsink
或yarn 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-destroy是ngneat许多很棒的库之一, 它使用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中方式的更多相关文章
- Angular的Observable可观察对象(转)
原文:https://blog.csdn.net/qq_34414916/article/details/85194098 Observable 在开始讲服务之前,我们先来看一下一个新东西——Obse ...
- EDM备忘录:触发式邮件订阅和退订功能介绍
一般来说,有触发式邮件订阅和退订功能是邮件模板设计中必不可少的两项功能,下面博主为大家介绍一下. 若客户在订阅后不想再收到这类邮件即可选择退订,将不再收到该IP地址的推广邮件,避免客户在继续收到这类邮 ...
- RxJS 简介:可观察对象、观察者与操作符
RxJS 简介:可观察对象.观察者与操作符 对于响应式编程来说,RxJS 是一个不可思议的工具.今天我们将深入探讨什么是 Observable(可观察对象)和 observer(观察者),然后了解如何 ...
- ngx通讯之可观察对象实现
1.公共服务 //test.service.ts import {Injectable} from '@angular/core'; import {Subject} from 'rxjs/Subje ...
- [译]RxJS 5.X基础篇
欢迎指错与讨论 : ) 当前RxJS版本:5.0.0-beta.10.更详细的内容尽在RxJS官网http://reactivex.io/rxjs/manual/overview.html.文章比较长 ...
- [译] 关于 Angular 依赖注入你需要知道的
如果你之前没有深入了解 Angular 依赖注入系统,那你现在可能认为 Angular 程序内的根注入器包含所有合并的服务提供商,每一个组件都有它自己的注入器,延迟加载模块有它自己的注入器. 但是,仅 ...
- wex5 教程 之 图文讲解 可观察对象的集群应用与绑定技术
一 前言: wex5官方教程里,开篇即以一个input输入,output即时输出的例子,为我们展现了一个概念:可观察对象.在以后我的项目开发中,将大量运用可观察对象. 那么,问题来了: 1. 可观察对 ...
- Angular2 Observable 可观察对象
可观察对象支持在应用中的发布者和订阅者之间传递消息.在需要进行事件处理,异步编程和处理多值的时候,可观察对象相对其他技术有显著的优点. 可观察对象是声明式的 —— 也就是说,虽然你定义了一个用于发布值 ...
- RxJS & Angular
RxJS & Angular https://www.learnrxjs.io/ https://rxjs-cn.github.io/learn-rxjs-operators/ https:/ ...
随机推荐
- “500 oops socket” Debian 9 running via Linux Deploy上成功部署vsftpd的解决方案(201901原创)【成功完美简单极致】
"500 oops socket" Debian 9 running via Linux Deploy上成功部署vsftpd的解决方案(201901原创)[成功完美简单极致] #自 ...
- node中 path.join 和 path.resovle 区别
console.log(__dirname) console.log('----') console.log(path.resolve(__dirname, '/a/b', '../')) conso ...
- 如何利用Intellij Idea搭建python编译运行环境 (转)
首先进入Intellij Idea的官方网站:点击打开链接 点击download,选择旗舰版进行下载.网上的破解教程很多,也可以注册一个学生账号拿到一年的免费试用权. 安装过程不再细说,第一次打开选择 ...
- C语言中左值和右值的区别(C语言学习笔记)
重要的内容要重复强调: C语言的术语Ivalue指用于识别或定位一个存储位置的标识符.( 注意:左值同时还必须是可改变的) 其实rvalue的发明完全是为了搭配lvalue , rvalue你可以理解 ...
- SAP内表类型及其数据读取效率评估
内表大概分3种: 1.标准表standard tables:如果不指定BINARY SEARCH附加选项,则默认为线性查找(linear search),既一条一条的查找. 2.排序表(sorted ...
- 关于SQL Server 镜像数据库快照的创建及使用
从SQL Server 2005 SP 起,SQL 开始支持数据库镜像.它的设计目的是试图为SQL Server 提供一个具有实时性数据同步的灾难恢复技术,即能够提供数据冗余备份,切换起来比较方便.每 ...
- Docker 如何动态修改容器端口映射
Docker端口映射往往是Docker Run命令时通过-p将容器内部端口映射到宿主机的指定端口上,一般来说容器的端口所对应的端口是提前确定需要映射的.但是有些情况下不得不需要临时映射端口,例如Doc ...
- Flask的配置文件加载两种方式
配置文件 1 基于全局变量 2 基于类的方式 配置文件的加载需要将配合文件的相对路径添加到app.config.from_object("文件路径"),类的方式也是一样,需要将类的 ...
- 简易双色球dome分享
代码如下: <style type="text/css"> div {font-weight: bold;text-align: center;} .tone{widt ...
- Rancher On K3s 高可用架构部署
Rancher 推荐部署架构 k3s 模式 RKE 和 k8s 模式 备注: 我对 RKE 的理解就是 Ansible + kubeadm 的打包,首先 rke 需要到每一个节点都可以免密 ssh , ...