Angular 5中如何在ngOnDestroy移除路由切换时的事件监听器?
Angular路由切换时无法移除事件监听器?这里有几个靠谱的解决办法
我懂这种 frustration!在React里用componentDidMount和componentWillUnmount处理事件监听简直行云流水,换到Angular却栽在ngOnDestroy移除监听器上,核心问题其实出在回调函数的引用一致性上——你绑定this后生成的是新函数,移除时找不到原监听器了。下面给你几个实用的解决方案:
方案1:保存绑定后的回调引用
直接把绑定this后的函数存为组件属性,这样添加和移除时用同一个引用:
import { Component, OnInit, OnDestroy } from '@angular/core'; @Component({ selector: 'app-my-component', template: '<p>My Component</p>' }) export class MyComponent implements OnInit, OnDestroy { // 保存绑定后的回调引用 private scrollHandler: () => void; ngOnInit(): void { // 绑定this并保存引用 this.scrollHandler = this.handleScroll.bind(this); window.addEventListener('scroll', this.scrollHandler); } ngOnDestroy(): void { // 用同一个引用移除监听器 window.removeEventListener('scroll', this.scrollHandler); } private handleScroll(): void { console.log('滚动事件触发', this); // this指向组件实例 } }
方案2:用箭头函数类属性(TypeScript友好)
箭头函数会自动绑定当前组件的this,而且它的引用是固定的,不用手动bind:
import { Component, OnInit, OnDestroy } from '@angular/core'; @Component({ selector: 'app-my-component', template: '<p>My Component</p>' }) export class MyComponent implements OnInit, OnDestroy { // 箭头函数自动绑定this,引用固定 private handleScroll = () => { console.log('滚动事件触发', this); // this指向组件实例 }; ngOnInit(): void { window.addEventListener('scroll', this.handleScroll); } ngOnDestroy(): void { window.removeEventListener('scroll', this.handleScroll); } }
方案3:用RxJS包装事件(最Angular的方式)
Angular生态里RxJS是标配,用它来处理DOM事件可以自动管理订阅,组件销毁时自动清理,不用手动调用removeEventListener:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { fromEvent, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-my-component', template: '<p>My Component</p>' }) export class MyComponent implements OnInit, OnDestroy { // 用于通知订阅取消的Subject private destroy$ = new Subject<void>(); ngOnInit(): void { // 用fromEvent把DOM事件转为Observable fromEvent(window, 'scroll') .pipe( // 组件销毁时自动取消订阅 takeUntil(this.destroy$) ) .subscribe(() => { this.handleScroll(); }); } ngOnDestroy(): void { // 发送销毁信号,清理所有订阅 this.destroy$.next(); this.destroy$.complete(); } private handleScroll(): void { console.log('滚动事件触发', this); // this指向组件实例 } }
为什么你之前的方法失效?
如果你的代码是这样的:
// 错误示例 ngOnInit() { window.addEventListener('scroll', this.handleScroll.bind(this)); } ngOnDestroy() { window.removeEventListener('scroll', this.handleScroll.bind(this)); }
每次调用bind(this)都会返回一个全新的函数引用,所以removeEventListener找的是第二个新函数,自然无法移除之前添加的监听器,内存泄漏也就来了。
内容的提问来源于stack exchange,提问作者Felipe Micali
相关产品推荐
相关产品推荐

