DOM节点数持续上涨即已发生泄漏——执行$$('*').length反复测试,若数值线性增长(如4218→4592→4931),结合Performance Monitor中DOM Nodes曲线只增不减或阶梯式上升,即可确认;再通过Memory面板对比快照定位Detached节点及Retaining Path。

DOM节点数失控不是“可能泄漏”,而是“已经泄漏”的明确信号——只要$$('*').length在反复操作后持续上涨,就说明有节点没被真正释放。
怎么快速确认是不是DOM节点在漏
打开 DevTools Console,直接执行$$('*').length。记下初始值(比如 4218),然后反复触发关键操作(如进战斗 → 退出 → 再进)。每轮后重跑该命令:如果数值线性增长(4218 → 4592 → 4931),就是 DOM 泄漏已发生。
- 别用
document.querySelectorAll('body *').length替代——它漏掉 Shadow DOM 和 iframe 内节点,而游戏/复杂 UI 常嵌 Canvas 或自定义渲染层 -
$$是 DevTools 原生快捷 API,结果最可靠,且无需额外加载库 - 这个值本身不等于内存 MB 数,但节点数单向增长是 DOM 泄漏最硬的证据
Performance Monitor里盯哪条曲线最有效
右下角三个点 → More Tools > Performance monitor,浮层里重点看 DOM Nodes 曲线:
- 正常应小幅波动:操作时升,GC 后回落至基线附近
- 异常模式包括:只增不减、回落缓慢拖尾、每点一次按钮就阶梯式跳一截
- 若
DOM Nodes涨而JS heap平稳,大概率是纯 DOM 引用残留(比如全局 Map 缓存了已移除的div) - 若两条曲线同步上涨,更可能是 JS 闭包 hold 住了 DOM 元素
怎么定位 Detached 节点的源头
在 Memory 面板拍两张堆快照:Snapshot #1(刚进主界面)、Snapshot #2(反复进出战斗 3 次后),切到 Comparison 视图:
立即学习“前端免费学习笔记(深入)”;
- 在 Constructor 列筛选
HTMLDivElement、HTMLCanvasElement、Text,看 Delta 是否为正且数量异常(如多出 187 个HTMLDivElement) - 点击某行,右侧 Retaining Path 出现
Closure→function→element或window.cacheUI→Map→key→detached div,就锁定了根因 - 常见陷阱:UI 系统用全局
Map缓存弹窗实例,销毁时只删 DOM 没删 Map 键;requestAnimationFrame回调里引用了已卸载面板的ref.current
console.dir() 和 getEventListeners() 怎么配合用
选中疑似泄漏的元素(比如某个 <button></button>),在 Console 执行:
-
console.dir($0):展开看真实 JS 属性,特别注意onclick、dataset、__vue、__reactFiber这类字段,以及手动挂的myCustomRef -
getEventListeners($0):返回按事件类型分组的对象(如{ click: [...] }),比翻属性更准——原生监听器可能绑定在父级但委托到该元素 - 若返回多个同类型监听器(如两个
click),大概率是重复绑定未清理;若监听器显示为function () { }且无名,说明来自匿名闭包,得回溯绑定位置
Detached 节点本身不显眼,但它的引用链往往藏在闭包或全局变量里——不点开 console.dir(),你永远看不到那条阻止 GC 的隐性引用。



















