javascript - 为什么一个 RxJS Subject 比多个事件监听器更快?

标签 javascript angular typescript rxjs dom-events

我最近发现页面的性能受到在其模板上多次使用的 Angular Directive(指令)的极大阻碍。在以下代码中找到了性能较慢的原因:

@HostListener('window:keydown', ['$event'])
private keydown(e: KeyboardEvent) {
     this.doSomething(e);
}

我怀疑问题可能是由于在窗口按键事件上注册了多个事件监听器引起的,因为每次在页面上重复该指令时都会注册一个新的事件监听器。为了验证该理论,我创建了一个带有 RxJS 主题的服务来处理该键盘事件:

@Injectable()
export class KeyboardService {
    constructor() {
        window.addEventListener('keydown', event => {
            this.keydownSubject.next(event);
        });
    }
}

private keydownSubject: Subject<KeyboardEvent> = new Subject<KeyboardEvent>();

get keydown(): Observable<KeyboardEvent> {
    return this.keydownSubject.asObservable();
}

然后我删除了指令中的@HostListener,并在 ngOnInit 中订阅了该服务的主题:

export class KeydownEventDirective implements OnInit, OnDestroy {
    constructor(private keyboardService: KeyboardService) {}

    private keydown(e: KeyboardEvent) {
        this.doSomething(e);
    }

    private keydownSubscription: Subscription;
    ngOnInit() {
        this.keydownSubscription =
            this.keyboardService.keydown.subscribe(e => {
                this.keydown(e);
            });
    }

    ngOnDestroy() {
        this.keydownSubscription.unsubscribe();
    }

    ...
}

该解决方案加快了页面速度,我很难发现为什么会这样。为什么 @HostListener 或向窗口的 keydown 事件添加多个事件监听器比对 RxJS Subject 的多个订阅对页面性能的影响更大?会不会是 Angular HostListeners 默认情况下不是被动监听器?

最佳答案

答案在于 Angular 对 Zone.js 的使用。 Angular 使用 Zone.js 进行变化检测。有关 Zone.js 如何工作的信息,我推荐 thoughtram.io 文章,Understanding ZonesZones in Angular .

最初的问题是在每次击键时页面的性能中发现的。为什么事件不能有效地处理它?问题出在 Angular 的变化检测上。 Zone.js 猴子用自己的函数修补 DOM 事件监听器注册。 Angular 利用了这一点,使每个带有监听器的 DOM 事件也触发更改检测。

当重复组件的多个实例在窗口上各自有自己的 @HostListener 时,它们各自独立触发 Angular 的变更检测。这导致 Angular 尝试检查整个应用程序,以检查每个监听键盘事件的组件在每次击键时的变化。考虑到这一点,出现性能问题也就不足为奇了。

KeyboardService 解决了这个问题,因为它只触发一次更改检测。只有一个监听器,RxJS Subject 将事件同步传递给单个 Zone.js 执行中的每个组件的订阅。

关于javascript - 为什么一个 RxJS Subject 比多个事件监听器更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45643327/

相关文章:

angular - 如何在返回 onSnapshot 结果的服务中创建一个方法,然后在我的组件或 Ionic 页面中使用它?

javascript - Angular:history.state.data 返回未定义而不是最后设置的数据对象?

javascript - 为什么调用嵌套在函数对象中的函数比...更快?

javascript - 您如何在 Jest 中手动模拟您自己的文件之一?

javascript - jquery 未从更改时的 select 语句获取值

angular - 从组件内部检查 Angular4 表单是否有效

angular - 如何以 Angular 格式化来自数据库的日期

angular - 如何删除以前使用 ng add 原理图命令添加的文件

javascript - 使用 WinJS 将图像保存到磁盘

javascript - 从提供者函数获取值到另一个页面