概述
本文将会先对RxJS进行阐述解释,然后会向大家逐一介绍一下以下的四个场景和使用到的语法:
- Angular 框架中使用
async pipe
将视图流直接映射为视图,以及tap
和map
的使用; - 同一可观察对象的多个订阅的解决方案 -
share()
运算符; - 使用
debounceTime()
和switchMap()
运算符实现动态搜索,以及switchMap()
结合interval()
做接口轮询; - 使用
takeUntil()
避免内存泄漏。
希望通过这四个简短的案例场景的解读,帮助大家对于RxJS有更好的体验感,并在日常的开发场景当中思考并寻找到属于自己的应用场景。
RxJS是什么
RxJS(JavaScript 响应式扩展)是一个使用可观察量进行响应式编程的库,可以更轻松地编写异步或基于回调的代码。
RxJS 提供了 Observable
类型的实现。该库还提供了用于创建和使用可观察量的实用函数。这些实用函数可用于:
- 将异步操作的现有代码转换为可观察量
- 迭代流中的值
- 将值映射到不同类型
- 过滤流
- 组合多个流
说人话,就是:让网页视图能够正确的响应相关的事件。
- 描述数据流
- 组合与转换数据流
- 消费数据流进行视图的更新
场景示例
原料准备:
1.在线运行环境:stackblitz.com/
2.在线mock数据请求:dummyjson.com/docs/produc...
场景1:Angular 框架中使用async pipe
将视图流直接映射为视图
在线示例:RxJS-场景1:async pipe
在上面的例子中,变量products$
从http请求中获取数据流。一旦数据处于可观察(observable)
的形式,除了基础的数据展示,我们还可以继续进行流操作/合并/链接等。
在 RxJS 中,理解生产者(Producer)
和消费者(Consumer)
的概念至关重要。这里的 Angular 模板是一个消费者 - 它订阅(在可观察变量上调用 .subscribe()
方法)products$
,并且每次执行此操作时,它都会创建一个生产者(https 请求)以将数据获取到可观察流中。
在Angular 框架中,可以直接使用async pipe
(在 html 模板中,片段| async
),它负责将可观察值中的值解析为普通数据,直接将视图流映射为视图。
如果希望更加考究一些,可以使用*ngIf
配合else
制作loading加载效果。虽然在实际项目开发中,我们所用的组件库一般都带有loading的配置项。
在线示例:RxJS-场景1:async pipe + else Template
另外,展开说一下文件里用到的tap
和map
。
tap
常常会和console.log
结合使用,能够帮助我们监控.pipe
管道中方法和方法之间的数据表现是什么样的。如下图所示:我们可以在控制台看到接口返回的数据格式。
在这个例子中,我们只需要将products
字段渲染在视图上,所以加上了map((data) => data.products)
,表示只需要返回其中products
字段。通常,当我们从后端获取的数据不能直接显示的时候,就可以使用map
来执行数据的过滤。
场景2:同一可观察对象的多个订阅的解决方案 - share() 运算符
在线示例:RxJS-场景2:share()
当我们建立多个请求相同内容的订阅者,或者模板中多次使用相同的可观察变量时,可以用share()
"共享"流。
1.建立多个请求相同内容的订阅者:制作一个变量productsWithApple$
渲染在页面上。
2.在模板中多次使用相同的可观察变量:在模板上多次渲染products$
。
场景3:使用debounceTime()
和switchMap()
运算符实现动态搜索
搜索功能是几乎每个网络应用程序中极其常见的功能。当输入框里输入/网络生成太多请求时,switchMap()
运算符会取消请求。通过取消请求,来节省浏览器/客户端的 CPU 功率,还可以节省实际的服务器资源。
同时,输入框里是连续输入事件,如果每次都响应会触发太多的后端请求,我们希望短时间内多次输入只触发最后一次,可以结合debounceTime()
运算符来实现。
场景3:RxJS-场景3:switchMap() 和 debounceTime()
另外,switchMap()
和interval()
结合,也可以更好的撰写轮询请求。(关于为什么直接用RxJS的interval()
而不是setInterval
可以看这篇文档:《Rxjs的interval和timer与Js的setInterval和setTimeout》)
主要的撰写思路就是:当下一次轮询执行请求的时候,上一个请求还在pending没有返回,就取消上一个请求。
typescript
component.ts
// 定时刷新告警数据
violationListInterval$: Observable<number> = interval(3000);
constructor() {
this.violationListInterval$.pipe(switchMap(() => this.api.getData()))
}
场景4:使用takeUntil()
避免内存泄漏
通常,我们手动订阅subscribe
,但忘记取消订阅。当然,在Angular
内置的async pipe
中(即场景1中的例子)会为我们自动取消订阅。
有两种办法来取消订阅。第一种,我们可以将subscribe()
的结果推入一个数组,然后在ngOnDestroy()
中遍历数据执行unsubscribe()
;第二种办法,就是使用takeUntil()
,在ngOnDestroy()
中关闭所有订阅。
这里着重说一下takeUntil
的使用。
在 RxJS
中,可以使用 takeUntil
来控制另外一个 Observable
对象数据的产生。使用 takeUntil
,上游的数据直接转手给下游,直到takeUntil
的参数吐出一个数据或者完结。
就像一个水龙头开关,一开始是打开的状态,上游的数据一开始直接流到下游,只要 takeUntil
一旦触发吐出数据,水龙头立刻关闭。
利用这点,可以在订阅时时,在管道中添加 takeUntil
,在组件销毁时吐出数据,这样这些订阅就会立刻关闭,也就达到回收内存的作用。
注意,takeUntil
一定要放在管道的最后,来提示之前所有的执行逻辑做一次销毁。否则,订阅无法被正常清除。
更多细节的讲解,可以看下这篇文章:《使用 takeUntil 操作符管理 Angular 组件的订阅》
资料
RxJS Quick Introduction (with Examples) for Beginner-Level Angular Developers
RxJs - Stream Analogs in Real Life (2021)