Skip to content

rxjs

定位

A javascript implementation for ReactiveX. 确实是事件界的 lodash.

术语

Its all about streams: The values/events that emitted over time

概念

observable

事件源 source / stream,既可以通过new Observable直接 create,也可以通过fromEvent等方法转换。

subject

在我心中会觉得就是 EventEmitter,即既可以触发事件,又可以监听事件。 有一些细分 比如 replaySubject 可以收到上一次 source 的所有触发消息。

subscribtion

通过observable.subscribe()进行订阅,这个订阅结果就叫 subscribtion.

operators

也是 rxjs 的卖点,有助于处理复杂的逻辑。

实例 operator: 帮助对事件源 emit 的 value 做一些变更,比如最常用的 map。 静态 operator: 一般是创造新的 observable,比如合并多个 observable。

operator 的使用还蛮优雅的,通过类似于 connect 的pipe链式调用

click$
  .pipe(map(event => event.code))
  .subscribe(console.log);

基础使用

const observable = new Observable(subscriber => {
  subscriber.next(1);
  subscriber.complete();
});

observable.subscribe({
  next(x) { console.log('got value ' + x); },
  error(err) { console.error('something wrong occurred: ' + err); },
  complete() { console.log('done'); }
});

拖动方块:通过一个函数就串通了触发条件 mousedown, 结束条件 mouseup,同时根据 mousemove 做操作,很好看了~

const $box = document.getElementById('box');
const mousedown$ = fromEvent($box, 'mousedown');
const mouseup$ = fromEvent($box, 'mouseup');
const mousemove$ = fromEvent($box, 'mousemove');

mousedown$
  .pipe(
    concatMap((startEvent: MouseEvent) => {
      const initialLeft = $box.offsetLeft;
      const initialTop = $box.offsetTop;
      // mousemove$才是最终监听的东西
      return mousemove$.pipe(
        takeUntil(mouseup$),
        map((evt: MouseEvent) => ({ x: evt.x - startEvent.x + initialLeft, y: evt.y - startEvent.y + initialTop })),
      );
    }),
  )
  .subscribe((evt: MouseEvent) => {
    $box.style.left = `${evt.x}px`;
    $box.style.top = `${evt.y}px`;
  });

使用目的 / 场景

operator有很多包装好的功能函数

用原生 JS 写一个聊天室,你需要维护一个队列池,和一个定时器,收到消息,先放进队列池,然后定时器负责把消息渲染出来、但是 rxjs 就有 buffer 的 operator:

Rx.Observable
    .fromEvent(ws, 'message')
    .bufferTime(1000)
    .subscribe(messages => render(messages))

好看的定时器

Observable.interval(1000).subscribe(() => {
  ...
})

尤其像cancatMapswitchMap这种高阶 operator,很好做「通过一个事件流返回另一个事件流的事」。

兼容所有事件源,抹平订阅差异

所有的 DOM 事件、ajax 请求、WebSocket、数组等等数据,统统可以封装成同一种数据类型。这就意味着,对于有多个来源的数据,我们可以每个数据来源都包装成 Observable,统一给视图层去订阅。

合并初始化与事件订阅

这个我还得再仔细看看怎么做~g

缓存数据

通过behaviorSubject,可以使得后续订阅它的部分都能拿到最新的数据

Excersize

Plain Observables are unicast, meaning every subscription is independent. To create multicast, you need to use Subject. (If you’re closing over a shared reference to a producer in your observable, it’s “hot” which refers to multicast; if you’re creating a new producer in your observable, it’s “cold”, which refers to unicast)

// default behavior with plain Observable
const observable = from([1,2,3])
observable.subscribe(console.log)
observable.subscribe(console.log)

with Subject, it works like Event Listeners in DOM world.

const subject = new Subject()
subject.subscribe(console.log)
subject.subscribe(console.log)

const observable = from([1, 2, 3])
observable.subscribe(subject)

Refs

入门: Gary Simon的这个60min课程不错 Youtube: Learn RxJS in 60 Minutes for Beginners

知乎专栏:DaoCloud 基于 RxJS 的前端数据层实践