Angular虚拟键盘导航返回时出现object unsubscribed错误求助
问题原因
你遇到的ERROR Error: object unsubscribed错误,本质是内存泄漏+未及时清理订阅导致的:
- 从第一个组件导航到第二个组件时,第一个组件会被销毁,但如果输入框未触发
blur事件(比如快速导航),指令的unsubscribeFromKeyboardEvents方法不会执行,旧的订阅仍然绑定在已销毁的指令实例上。 - 返回第一个组件时,新的指令实例创建并订阅键盘事件,此时服务发送事件时,旧的已销毁实例的订阅会被触发,导致报错。
- 当前代码在调用
unsubscribe()时未检查订阅对象是否存在,极端情况下可能引发空指针问题。
修复方案
1. 实现ngOnDestroy生命周期钩子
在指令中添加OnDestroy接口,确保组件销毁时强制清理所有订阅,同时清理创建的DOM元素。
修改后的完整指令代码
import { Directive, OnInit, OnDestroy, ElementRef, HostListener } from "@angular/core"; import { KeyboardService } from "./keyboard.service"; import { Subscription } from "rxjs"; @Directive({ selector: "[appOskInput]" }) export class OskInputDirective implements OnInit, OnDestroy { private keySubscription: Subscription; private backspaceSubscription: Subscription; private enterSubscription: Subscription; private measure: HTMLElement; constructor(private el: ElementRef, private keyboard: KeyboardService) {} ngOnInit() { let thisStyle = window.getComputedStyle(this.el.nativeElement); this.measure = document.createElement("span"); this.measure.style.position = "absolute"; this.measure.style.right = "100%"; this.measure.style.font = thisStyle.font; document.body.appendChild(this.measure); } ngOnDestroy() { this.unsubscribeFromKeyboardEvents(); // 清理手动创建的DOM元素,避免内存泄漏 if (this.measure?.parentNode) { this.measure.parentNode.removeChild(this.measure); } } @HostListener("focus") private onFocus() { this.keyboard.fireKeyboardRequested(true); this.subscribeToKeyboardEvents(); } @HostListener("blur") private onBlur() { this.keyboard.fireKeyboardRequested(false); this.unsubscribeFromKeyboardEvents(); } private subscribeToKeyboardEvents() { // 订阅前先清理旧订阅,防止重复订阅 this.unsubscribeFromKeyboardEvents(); this.keySubscription = this.keyboard.keyPressed.subscribe(key => this.onKey(key) ); this.backspaceSubscription = this.keyboard.backspacePressed.subscribe(_ => this.onBackspace() ); this.enterSubscription = this.keyboard.enterPressed.subscribe(_ => this.onEnter() ); } private unsubscribeFromKeyboardEvents() { // 用可选链避免空指针错误 this.keySubscription?.unsubscribe(); this.backspaceSubscription?.unsubscribe(); this.enterSubscription?.unsubscribe(); // 重置订阅引用,避免重复取消 this.keySubscription = null; this.backspaceSubscription = null; this.enterSubscription = null; } // 补充原代码缺失的核心功能方法 private onKey(key: string) { this.el.nativeElement.value += key; this.updateMeasure(); } private onBackspace() { this.el.nativeElement.value = this.el.nativeElement.value.slice(0, -1); this.updateMeasure(); } private onEnter() { this.el.nativeElement.blur(); } private updateMeasure() { this.measure.textContent = this.el.nativeElement.value; this.el.nativeElement.style.width = this.measure.offsetWidth + "px"; } }
2. 额外优化说明
- 在
subscribeToKeyboardEvents开头先清理旧订阅,避免同一输入框多次触发focus导致重复订阅。 - 使用可选链操作符
?.简化空值检查,让代码更简洁安全。
这样修改后,无论组件是通过blur事件还是销毁触发,都能确保订阅被正确清理,彻底解决object unsubscribed错误,同时避免内存泄漏。
内容的提问来源于stack exchange,提问作者RRB
相关产品推荐
相关产品推荐

