[译]Rxjs&a ad8 mp;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( ad8 { 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.c ad0 omponentDestroyed$.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许多很棒的库之一, 它使 15b0 用
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仓库吧!
总结
上面我们已经看到来许多订阅和退订可观察对象方式, 每个都各有各的优劣并且有着不同的编码风格.
但是最重要是不管我们选择那种方式, 我们都要保持编码风格的一致
- Angular使用可观察对象的数据架构
- 这里想经过一个小程序研究标准库为 vector 对象提供的内存分配策,因为vector容器比list和deque容器用的很多,而且它的存储方式是连续的
- FormData()方法 ——将数据以Key=value 的方式通过XMLHttpRequest对象发送给服务器
- ajax中XMLHttpRequest对象的open()方法GET和POST方式区别
- java 在运行时识别对象和类的信息的方式
- Java——深入理解Class对象(二):Class对象的加载及其获取方式
- Java 创建对象的四种方式
- JAVA中创建对象的五中方式
- JS 创建对象的几种方式
- js中创建对象三种方式
- JS7种方式创建对象详解(下)
- JavaScript中访问id对象.属性的方式访问属性
- JavaScript创建对象方式研究
- Java遍历Map对象的四种方式
- 组件方式开发 Web App全站-5-开发H5对象
- java中创建对象的5种方式
- java中对象的两种访问方式
- UITableViewCell对象的创建方式
- Java中对象拷贝的两种方式
- jQuery中json对象的复制方式介绍(数组及对象)