何时使用AbortController移除事件监听器?
AbortController 除了大家熟知的取消fetch请求的能力外,批量清理事件监听器的特性可以大幅简化代码、避免内存泄漏,使用方式也很简单:只需要在调用addEventListener时的配置项里传入signal: controller.signal,就能把事件和控制器关联,调用controller.abort()时所有关联事件会被自动移除。
常见的实际使用场景如下:
单页应用组件/页面卸载清理
现在前端框架的组件生命周期里,经常会在挂载时给全局对象(window、document)绑定事件,比如滚动监听scroll、窗口大小调整resize、全局键盘事件keydown等,在组件卸载时如果不清理就会造成内存泄漏。以前需要手动存储每一个事件 handler 引用,再逐个调用removeEventListener清理,现在只需要给这些事件都绑定同一个AbortSignal,组件卸载时调用一次controller.abort()就能一次性清除所有关联事件,完全不需要维护handler引用。
比如拖拽类组件通常会绑定mousedown、mousemove、mouseup、keydown(ESC取消拖拽)四个事件,用这种方式清理只需要一行代码就能完成,不会出现漏清的问题。临时交互场景的事件回收
很多临时出现的交互模块都会绑定专属事件,比如弹窗打开时绑定的点击空白关闭、ESC关闭、窗口大小变化调整弹窗位置的事件;右键自定义菜单打开时绑定的点击空白关闭、滚动关闭事件;下拉框展开时绑定的全局点击关闭事件。这类场景下事件只在模块激活时生效,模块关闭时直接调用abort就能清空所有临时绑定的事件,逻辑非常简洁。复杂交互模式的状态切换
做编辑器、绘图工具这类有多种交互模式的应用时,不同模式下绑定的事件完全不同:比如绘制矩形模式下会绑定画布的mousedown、mousemove、mouseup、mouseleave事件,切换到橡皮擦模式时,需要先把矩形模式的所有事件全部移除再绑定新的事件。用AbortController的话,每个模式对应一个controller,切换模式时直接abort上一个模式的controller就能完成清理,不需要特意记录上一个模式绑定了哪些事件,避免旧事件残留导致的逻辑bug。多关联事件的异步任务终止
比如文件上传、音视频录制这类功能,除了核心的请求可以用abort终止外,通常还会绑定进度更新监听、暂停按钮点击监听、取消按钮点击监听、网络状态变化监听等多个关联事件。用户触发取消操作时,一次abort调用就能同时终止请求、清除所有关联的事件监听器,不需要分开处理请求取消和事件清理两套逻辑。
内容的提问来源于stack exchange,提问作者Monster Cat

