繁体   English   中英

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

[英]RxJS, why does fromEvent() register a new event for each subscription?

假设我们有以下代码

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$ ,注册到文档的事件数也会随着每次订阅而增加。

调试器截图

相比之下,无论我添加到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并且默认情况下是共享的。
  • 在Hot Observable的情况下,订户仅从订阅时开始接收事件。 在冷可观察的情况下,订户接收观察者可以产生的所有事件。
  • share()运算符可用于使冷可观察到热。

谢谢!

正如您可以通过单击run code snippet看到的那样, 答案是肯定的 ,每次subscribe click$ stream时都会附加侦听器。

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

 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> 

正如您在上一个示例中所看到的,日志registering listener器只发生一次。 有关the share operator更多信息

希望能帮助到你!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM