# 为监听器指定唯一的负责人和生命周期

> 通过为每个场景的事件侦听器提供明确的所有者、设置边界和拆卸路径来停止重复输入和回调。

- URL: https://oneshotgems.ai/zh-Hans/topics/preventing-game-event-listener-duplicates
- 类别: 游戏开发者指南
- 类型: 指南
- 更新日期: 2026年10月5日

如果重新启动后一次单击触发两次，则每次场景打开时都可能会注册一个侦听器，并且永远不会删除该侦听器。这种明显的重复动作症状与广泛的内存泄漏调查不同：首先计算注册并将它们与场景生命周期联系起来。每个侦听器都应该有一个明确的所有者、一个为该所有者运行一次的设置点，以及当场景停止接受输入时的拆卸路径。

## 在有意的生命周期边界处注册

避免从渲染函数或每个帧附加窗口 keydown 处理程序。将场景特定的设置放入启动或安装函数中，并确保该函数在没有清理的情况下不能运行两次。使用removeEventListener时保留对回调的引用；重复创建的匿名回调以后很难匹配。或者，为一组相关处理程序提供一个 AbortController 并在侦听器选项中传递其信号，然后在场景结束时中止该组。

仔细选择活动目标。全局窗口侦听器可能适合游戏处于活动状态时的按键，但按钮的单击处理程序属于该按钮。不要为同一操作同时注册父委派处理程序和直接处理程序，除非事件传播是设计的故意一部分。

## 测试重复的场景输入作为症状检查

打开一个场景，按一次其动作，离开，然后再次打开它。一种输入仍应产生一种效果。重复该循环五次，并验证每次按下响应计数是否保持为 1；这是功能检查，而不是内存测量。还要确认旧场景的输入在退出后不再改变其分数或状态。 MDN 记录了 AbortSignal 对分组侦听器的支持，并指出匿名函数需要保留引用才能显式删除。

## 使拆卸与所有权保持一致

当场景被替换时，在处理它们引用的对象之前中止或删除其侦听器。如果某些侦听器属于整个应用程序 shell，请将它们保留在寿命较长的所有者中，而不是在每轮中删除它们。添加围绕重新启动、暂停和返回菜单流程的回归案例，因为每个转换可能会意外地安装同一场景两次或跳过其清理。

## 常见问题

**为什么一键按下会产生多个游戏动作？** 同一操作可能有多个已注册的处理程序，或者祖先和子级都可以处理该事件。在场景生命周期边界计算注册并检查事件传播。

**AbortController 可以删除事件监听器吗？** 是的。添加具有相同信号的相关侦听器，并在其所有者结束时调用中止。这为团队提供了清晰的拆卸路径；它不能取代决定由哪个所有者控制它们。

## 跟随好奇心探索

- [追踪浏览器游戏中的内存泄漏](https://oneshotgems.ai/zh-Hans/topics/game-memory-leaks): 使用可重复的游戏和清理检查来查找保留的游戏场景、侦听器、计时器和随时间累积的资产。
- [管理浏览器游戏中的焦点](https://oneshotgems.ai/zh-Hans/topics/game-focus-management): 通过为游戏提供可见的焦点状态并在覆盖关闭后恢复焦点，使键盘输入、菜单和返回路径可预测。

## 延伸阅读

- [MDN addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)

---
规范页面: https://oneshotgems.ai/zh-Hans/topics/preventing-game-event-listener-duplicates
