RxJS,为什么fromEvent()为每个订阅注册一个新事件?

时间:2019-03-30 11:27:05

标签: javascript rxjs

假设我们有以下代码

const clickEvent$ = fromEvent(document, 'click').pipe(
    pluck('target')
);

clickEvent$.pipe(
    filter(node => node.id === 'button1')
).subscribe(() => {
    console.log('Button 1 clicked!');
});

clickEvent$.pipe(
    filter(node => node.id === 'button2')
).subscribe(() => {
    console.log('Button 2 clicked!');
});

当我查看调试器中的已注册事件时,我看到在文档上注册了两个单击事件。当clickEvent$的订阅数量增加时,注册到文档的事件数量也随着每次订阅而增加。

debugger screenshot

相比之下,无论我添加到switch语句中有多少种情况,下面的代码仅记录一个事件。

document.addEventListener('click', (event) => {
    switch (event.target.id) {
        case 'Button1':
            console.log('Button 1 clicked!');
            break;
        case 'Button2':
            console.log('Button 2 clicked!');
            break;
    }
});

所以我的问题是:-

  • 为什么每个对clickEvent $(或任何DOM事件流)的订阅都会向文档添加一个新事件。
  • 订阅数超过100时,它如何影响性能
  • 有没有办法解决这个问题,只将一个事件侦听器添加到文档中。

这就是我对热和冷可观测物的了解

  • 从DOM事件创建的所有可观察序列均为Hot,默认情况下为共享。
  • 在可观察到的情况下,订阅者仅从订阅时开始接收事件。而在可观察的天气很冷的情况下,订户会收到可观察到的所有事件。
  • share()运算符可用于使可观察到的寒冷变热。

谢谢!

1 个答案:

答案 0 :(得分:2)

通过点击onclick可以看到,答案是肯定的,每次您run code snippetsubscribe流时都会附加监听器。

  
    

每次订阅产生的Observable时,事件处理函数都会在给定的事件类型上注册到事件目标。当该事件触发时,作为第一个参数传递给已注册函数的值将由输出Observable发出。取消订阅Observable时,将从事件目标中取消注册功能。

  

click$
const { fromEvent } = rxjs;
const { mapTo } = rxjs.operators;

const target = document.getElementById('test');
/* ignore, debug */
const $delegate = target.addEventListener;

target.addEventListener = (...args) => {
  console.log('registering listener');
  
  return $delegate.apply(target, args);
};
/* // */

const click$ = fromEvent(target, 'click');

click$.pipe(
    mapTo('stream 1: click'),
).subscribe(console.log);

click$.pipe(
    mapTo('stream 2: click'),
).subscribe(console.log);

您可以创建<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.js"></script> <button id="test">click here</button>流来避免这种情况。

shared
const { fromEvent } = rxjs;
const { mapTo, share } = rxjs.operators;

const target = document.getElementById('test');
/* ignore, debug */
const $delegate = target.addEventListener;

target.addEventListener = (...args) => {
  console.log('registering listener');
  
  return $delegate.apply(target, args);
};
/* // */

const click$ = fromEvent(target, 'click').pipe(
  share(), // <= share operator
);

click$.pipe(
    mapTo('stream 1: click'),
).subscribe(console.log);

click$.pipe(
    mapTo('stream 2: click'),
).subscribe(console.log);

如您在最后一个示例中所见,日志<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.js"></script> <button id="test">click here</button>仅发生一次。有关the share operator

的更多信息

希望有帮助!