Подтвердить что ты не робот

Почему объект RxJS быстрее, чем несколько прослушивателей событий?

Недавно я обнаружил, что производительность страницы сильно затруднялась директивой angular, которая несколько раз использовалась в его шаблоне. Причина более низкой производительности была обнаружена в следующем фрагменте кода:

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

Я подозревал, что проблема может быть вызвана регистрацией нескольких прослушивателей событий в событии keydown окна, потому что новый был зарегистрирован каждый раз, когда эта директива повторялась на странице. Чтобы проверить эту теорию, я создал службу с RxJS Subject для обработки этого события клавиатуры:

@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? Может ли быть, что angular HostListeners не являются пассивными слушателями по умолчанию?

4b9b3361

Ответ 1

Вероятно, вы пытаетесь поймать одно и то же событие на нескольких компонентах, что приведет к выполнению всех ваших методов подписки (private keydown(e: KeyboardEvent)) независимо от того, какой ключ был нажат, поскольку вы добавляете прослушиватель событий в глобальное окно уровень.

Это намного лучше подходит к тому, что вы делали в своем сервисе, хотя я уверен, что вы также можете использовать

public emitter: EventEmitter<any> = new EventEmitter<any>();

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

private doSomething(event: any): void {
     this.emitter.emit(event);
}

внутри вашей службы, а затем публичные EventEmitter или Subject, на которых ваши компоненты могут подписаться и поймать событие keyDown.

Ответ 2

Это не связано с субъектом. Во-первых, инъекционная услуга является singleton, поэтому для всех ваших директив будет предоставлен один экземпляр, второй в этой однопользовательской службе вы регистрируете единственный метод в конструкторе для обработки события keydown, которое вызывает субъект, если вы напечатаете в консоли сообщение, вы увидите, что будет один вызов.

Но с помощью Hostlistener по директиве будет зарегистрировано событие с тегом и множественное выполнение при событии keydown.