概述
- RxJS 是一个用于处理异步和基于事件编程的库,其核心理念是使用可观察序列(Observable Sequences)来构建程序逻辑
- 你可以把它看作是处理事件的“lodash”,类似于 lodash 对数组和对象的操作,RxJS 则是对事件流的操作
1 )学习资源推荐
目前中文社区中有几个关于 RxJS 的文档网站可供参考:
-
cn.rx.js.org 官方文档,内容详尽,结构清晰;
-
rx.nodejs.cn 中文社区翻译版,适合中文用户;
-
rxjs.tech 翻译质量相对较高,推荐使用。
-
rxjs.dev 官网
-
需要注意的是,文档中某些功能在文档中标记为划线
-
表示这些方法可能已被弃用,或在未来的版本中不再支持
2 )核心概念解析
-
官方介绍中提到:RxJS 是一个使用可观察序列编写异步和基于事件程序的库
-
这句话可能初看有些晦涩,但其核心聚焦点在于两个方面:
- 可观察序列(Observable Sequences)
- 异步/事件处理能力
-
为了更好地理解 RxJS,我们需要先了解其几个核心类型:
- Observable(可观察对象):类似于 Promise,但它可以发出多个值,而不是只能解决一次;
- Observer(观察者):用于监听 Observable 发出的值;
- Subject(主体):一种特殊的 Observable,用于广播事件;
- Operators(操作符):如 map、filter、reduce 等,用于处理事件流;
- Scheduler(调度器):控制异步任务的执行方式,属于进阶概念。
-
这些核心类型和操作符构成了 RxJS 的基础,使得我们可以将异步事件当作集合一样进行操作和处理
3 )实践示例:从事件监听到流式处理
-
我们以一个简单的 DOM 事件监听为例,来看看 RxJS 如何处理事件
-
传统的写法是:
document.addEventListener('click', () => { // 处理点击事件 });
-
而使用 RxJS 后:
import { fromEvent } from 'rxjs'; fromEvent(document, 'click').subscribe(() => { console.log('Clicked!') });
-
这里的
fromEvent
返回一个 Observable,通过subscribe
方法进行订阅 -
你也可以传入一个 Observer 对象,它包含三个回调方法:
next()
:每次事件触发时调用;error()
:发生错误时调用;complete()
:流结束时调用。
-
这种写法不仅结构清晰,而且支持链式调用,便于管理状态
4 )操作符与流式控制:Purity 与 Flow
4.1 Purity
- Purity(纯净性) 是 RxJS 的一大优势,它允许我们使用纯函数来生成值
- 创建一个不纯的函数,举例,通常会弄乱你的状态
let count = 0; document.addEventListener('click', () => console.log(`Clicked ${++count} times`));
- 例如,我们可以使用
scan
操作符来实现点击计数,可以隔离状态import { fromEvent, scan } from 'rxjs'; import { scan } from 'rxjs/operators'; fromEvent(document, 'click') .pipe(scan((count) => count + 1, 0)) .subscribe(count => console.log(`Clicked ${count} times`));
- 在这个例子中,
scan
类似于数组的reduce
方法,每次点击都会将计数值递增,初始值是 0 - 它可以把状态量在链式写法里面执行,不需要再外层单独定义了
4.2 Flow
Flow(流动) 是 RxJS 中另一个核心概念,它表示事件流的传递与变换
我们可以使用 pipe
函数将多个操作符串联起来,实现复杂的事件处理逻辑:
它把嵌套的函数调用变成了一种管道内参数传递的方式
下面是使用纯 JavaScript 实现“最多允许每秒单击一次”的方式:
let count = 0;
let rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', () => {
if (Date.now() - lastClick >= rate) {
console.log(`Clicked ${++count} times`);
lastClick = Date.now();
}
});
使用 RxJS:
import { fromEvent, throttleTime, scan } from 'rxjs';
fromEvent(document, 'click')
.pipe(
throttleTime(1000),
scan((count) => count + 1, 0)
)
.subscribe((count) => console.log(`Clicked ${count} times`));
- 这里本质上是一个节流操作,这里的
pipe
方法让代码更具有可读性 - 将嵌套的函数调用转化为参数传递的方式,使得事件流的处理逻辑更加清晰
- 操作符中的管道文档 operators#piping
5 ) Values 与值的处理
Values(值转换) 是 RxJS 中处理流中数据的方式
可以通过你的 observables 传来的值进行转换
以下是使用纯 JavaScript 来为每次单击增加当前鼠标 x 位置的方法
let count = 0;
const rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', (event) => {
if (Date.now() - lastClick >= rate) {
count += event.clientX;
console.log(count);
lastClick = Date.now();
}
});
使用 RxJS之后
import { fromEvent, throttleTime, map, scan } from 'rxjs';
fromEvent(document, 'click')
.pipe(
throttleTime(1000),
map((event) => event.clientX),
scan((count, clientX) => count + clientX, 0)
)
.subscribe((count) => console.log(count));
-
这里使用了
map
操作符提取事件中的值: -
RxJS 提供了大量操作符
-
这些操作符都可在官方文档的 Operators 分类中找到详细说明
6 ) 总结
RxJS 的几个核心概念
- Observable、Observer、Subject、Operator、Scheduler
- 如何使用 fromEvent 创建事件流
- 如何通过 pipe 串联操作符处理事件流
- 如何使用 scan、map、filter、throttleTime 等操作符进行事件处理
- RxJS 与传统回调方式的区别:链式结构、状态管理、可维护性增强