Angular中使用rxjs的fromEvent如何获取keyup的最后一次触发事件
问题根源
- 你把事件订阅逻辑放在了
ngAfterContentChecked生命周期钩子中,这个钩子会在Angular每次执行变更检测时反复触发,每触发一次就新增一个keyup事件的订阅,最终按一次按键会触发多个订阅的回调,输出多条日志。 - 你当前的
distinctUntilChanged没有传入对比逻辑,默认只对比引用,而每次keyup产生的事件对象都是新的引用,所以这个操作符现在完全没有生效。
修复方案
- 将事件订阅逻辑移到只会执行一次的
ngAfterViewInit生命周期中,避免重复订阅 - 补充订阅销毁逻辑,避免组件销毁后内存泄漏
- 按需调整
distinctUntilChanged的对比规则,只对比你关心的字段(比如按键值、输入框内容等) - 如果需要捕获最后一次触发结果,保留
debounceTime即可,它会在你停止触发事件指定时长后,抛出最后一次的事件对象
修复后的代码参考:
import { Directive, AfterViewInit, ElementRef, OnDestroy } from '@angular/core'; import { fromEvent, Subject } from 'rxjs'; import { distinctUntilChanged, debounceTime, takeUntil } from 'rxjs/operators'; @Directive({ selector: '[appFocuser]' }) export class FocuserDirective implements AfterViewInit, OnDestroy { focusableArray = []; parent = null; currentFocus: HTMLElement; // 用于销毁订阅的subject private destroy$ = new Subject<void>(); constructor(private el: ElementRef) {} ngAfterViewInit() { this.parent = this.el.nativeElement; this.initFocus(); this.initKeyupHandler(); } // 单独抽离焦点初始化逻辑,有需要可以单独调用 private initFocus() { this.focusableArray = Array.from(this.parent.querySelectorAll(`[data-focus=true]`)); this.currentFocus = this.focusableArray[0] as HTMLElement; if (this.currentFocus) { this.currentFocus.focus(); } } private initKeyupHandler() { fromEvent<KeyboardEvent>(this.parent, 'keyup') .pipe( debounceTime(100), // 这里示例对比按键code,你可以根据需求改成对比输入框的值等逻辑 distinctUntilChanged((prev, curr) => prev.code === curr.code), // 组件销毁时自动取消订阅 takeUntil(this.destroy$) ) .subscribe(event => { // 这里只会输出防抖后的最后一次事件 console.log(event); // 你的后续业务逻辑 }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } }
内容的提问来源于stack exchange,提问作者3gwebtrain
相关产品推荐
相关产品推荐

