Rx.js绝对称得上是Angular中的一个亮点,来看看金雄同学的分享,以熟悉rxjs固定的代码结构为突破口,掌握rxjs
漫谈 rxjs 学习经历
在学会 rxjs 之前,我也是翻阅了大量的 rxjs 资料,每位作者好像都说的又对又好,但是我还是不会。直到偶尔翻到了这篇文章RxJS 快速入门,在此感谢作者。虽说前期学而不会的过程也是有用的,不是直接吃第三个馒头就能饱,但是这篇文章大大提前了我学会 rxjs 的时间。它帮我第一次认识了 rxjs,相当于一个基座,在这个基座上我开始建立自己对 rxjs 的理解。由感而发,写了这篇文章给大家分享一下。
为什么要学 rxjs
- 高热度,相信前端同学没有人愿意让这样的热点成为自己的盲区
- 项目中遇到 rxjs 代码,至少要能看懂才行
- rxjs 的优势:RxJS 是使用 Observables 的响应式编程的库,它使编写异步或基于回调的代码更容易。RxJS 的强大之处正是它使用纯函数来产生值的能力。这意味着你的代码更不容易出错。
rxjs 是什么
rxjs,和过去的 lodash 一样,是 js 的一个工具包。
其他有关 rxjs 是什么的说法,不要管了,都是给初学者增加上手难度的。
为你建造学习 rxjs 的基座
import { of, filter, map } from 'rxjs';
of(1, 2, 3)
.pipe(
filter(item => item % 2 === 1),
map(item => item * 3)
)
.subscribe(item => console.log(item));
这是一段 rxjs 常见代码。
of() 称为创建器(creator),它会创建一个 Observable 类型的对象,也就是创建流。
pipe()是 Observable 对象的方法。它的包裹的 filter 和 map 称为操作符(operator),用来对数据条目进行处理。这些操作符被当作 pipe 方法的参数传进去。
subscribe()也是 Observable 对象的方法,表示消费者要订阅这个流,当流中出现数据时,传给 subscribe 方法的回调函数就会被调用,并且把这个数据传进去。这个回调函数可能被调用很多次,取决于这个流中有多少条数据。Observable 必须被 subscribe 之后才会开始生产数据。如果没人 subscribe 它,那就什么都不会做。
建议以此为基座,开始 rxjs 的学习。 为什么强调这个基座呢? 因为 rxjs 的使用,都是这种模式化的三段式的结构,也就是说使用 rxjs 时,一般都是先创建 Observable 对象,然后调用 pipe 方法(即调用各种操作符对数据流进行处理),最后用 Observable 对象的 subscribe 方法接收(订阅)处理结果。 理解了这种结构,我们就可以举一反三地掌握 rxjs 其他知识点。
在基座上构建 rxjs 知识体系
贯穿 rxjs 的两种设计模式
- 观察者模式
rxjs 用到了设计模式中的观察者模式。如果还不了解也不要担心,观察者模式很好理解,它有另一个名字——发布订阅模式。 我们常看的天气预报就是一个发布者,查看天气预报的各位就是订阅者。预告下雨,你看到后就会带伞。这就是一次完整的发布订阅行为,"预告"是发布者,"下雨"是数据,"你"是订阅者,"看"是订阅行为,"带伞"是你的响应。 再次回到刚才的代码, of(1, 2, 3)
就是创建消息发布者,也就是创建 rxjs 中的 Observable 对象,Observable 英文原意是可观察的,和发布者意思相近。 .pipe( filter(item => item % 2 === 1), map(item => item * 3) )
是数据的处理过程 .subscribe(item => console.log(item))
是订阅者对消息的订阅行为,以及收到订阅消息后的响应行为。 item => console.log(item)
中的 item 就是pipe
管道处理后的数据,函数体console.log(item)
则是响应。
- 迭代器模式
迭代器(Iterator)模式又叫游标(Sursor)模式,迭代器具有 next 方法,可以顺序访问一个聚合对象中的各个元素,而不需要暴露该对象的内部表现。迭代器模式可以把迭代的过程从从业务逻辑中分离出来,迭代器将使用者和目标对象隔离开来,即使不了解对象的内部构造,也可以通过迭代器提供的方法顺序访问其每个元素。
使用 rxjs 时,会经常感觉到这是一个遍历、加工可迭代数据的工具库。
操作符
rxjs 中的操作符分为创建操作符(Creation Operator)和管道操作符(Pipeable Operator),前者用于创建 Observable 对象,后者用于处理数据流。 rxjs 5.5 版本之后,操作符都是一些独立的函数,而不是挂载在对象上。这个特点,大家可以参考一下这篇文章重学 Rxjs —— pipe 与链式调用,来进一步了解。
留一个问题:发现许多操作符的作用和 JS 原生 api 一样,那么为什么 rxjs 要重复造轮子,创建那么多内置操作符,而不是在订阅到数据后用 JS 原生 api 处理呢?
import { of } from 'rxjs';
const sumObserver = {
sum: 0,
next(value) {
console.log('Adding: ' + value);
this.sum = this.sum + value;
},
error() {
// We actually could just remove this method,
// since we do not really care about errors right now.
},
complete() {
console.log('Sum equals: ' + this.sum);
},
};
of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.
.subscribe(sumObserver);
// Logs:
// "Adding: 1"
// "Adding: 2"
// "Adding: 3"
// "Sum equals: 6"
实战中的典型例子
import { ajax } from 'rxjs/ajax';
import { map, catchError, of } from 'rxjs';
const users = ajax({
url: 'https://httpbin.org/delay/2',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'rxjs-custom-header': 'Rxjs',
},
body: {
rxjs: 'Hello World!',
},
}).pipe(
map(response => console.log('response: ', response)),
catchError(error => {
console.log('error: ', error);
return of(error);
})
);
users.subscribe({
next: value => console.log(value),
error: err => console.log(err),
complete: () => console.log('complete!'),
});
- subscribe()的入参是一个 Observer 对象,这是当前官方推荐的写法。
- next()函数的入参 value,即经过 pipe()函数处理后的一条数据。
- error()函数用于捕获并处理错误。
- complete()在数据流成功处理完成后执行一次,和 error()是互斥的。
Tips:rxjs 各种文档里面一般不会对 API 做详细介绍,需要了解 API 用法的话,建议直接通过代码追踪找到源码,源码里有 API 的详细讲解。
遗留问题:catchError 和 Observer 中的 error 有什么区别?