自定义Modal元素无法被垃圾回收,如何正确实现避免该问题?
问题根源
页面中的<dialog>是持久存在的DOM元素,每次创建Modal自定义元素实例并挂载时,都会在connectedCallback方法中给这个<dialog>新增一个close事件监听器。这些监听器不会随Modal实例从DOM中移除而自动销毁,因此每次关闭模态框时,所有已添加的监听器都会被触发,导致打印次数累加。同时,监听器的箭头函数会捕获connectedCallback中的this(即当前Modal实例),形成闭包引用,导致实例无法被垃圾回收,造成内存泄漏。
解决方案
在Modal类中实现disconnectedCallback钩子,当实例从DOM中被移除时,主动移除绑定在<dialog>上的事件监听器,同时解除监听器函数的引用,帮助垃圾回收机制回收实例。
修改后的代码如下:
class Modal extends HTMLElement { connectedCallback() { this.innerHTML = `<button onclick="this.closest('dialog').close()">Close modal</button>`; // 保存监听器函数的引用,方便后续移除 this.closeHandler = () => { console.log(`Modal closed`); }; this.closest(`dialog`).addEventListener(`close`, this.closeHandler); } disconnectedCallback() { // 当实例从DOM移除时,移除对应的事件监听器 if (this.closeHandler) { this.closest(`dialog`).removeEventListener(`close`, this.closeHandler); // 解除引用,帮助垃圾回收 this.closeHandler = null; } } } customElements.define(`app-modal`, Modal);
补充说明
disconnectedCallback是自定义元素的生命周期钩子,当元素从DOM树中被移除时会自动触发,正好用来清理绑定在外部元素上的事件监听器。- 必须保存监听器函数的引用,因为
removeEventListener需要传入与addEventListener完全相同的函数引用才能生效,匿名函数无法被移除。
内容的提问来源于stack exchange,提问作者RobertAKARobin
相关产品推荐
相关产品推荐

