为什么我的自定义元素没有被垃圾收集?

问题描述 投票:0回答:2

考虑一个包含触发模式打开的按钮的网页:

<dialog></dialog>

<button>Open modal</button>
const trigger = document.querySelector(`button`);
const wrapper = document.querySelector(`dialog`);

trigger.addEventListener(`click`, () => {
  wrapper.replaceChildren(new Modal());
  wrapper.showModal();
});

模态是自定义元素:

class Modal extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `<button onclick="this.closest('dialog').close()">Close modal</button>`;
    
    this.closest(`dialog`).addEventListener(`close`, () => {
      console.log(`Modal closed`);
    });
  }
}

customElements.define(`app-modal`, Modal);

这是一个演示:https://codepen.io/RobertAKARobin/pen/OJGJQro

我期望的是每次单击

Close modal
按钮时,控制台都会记录
'Modal closed'

实际发生的是控制台记录

'Modal closed'
一次,每次到目前为止模式已经关闭。因此,单击 1 时,它会记录消息 1 次,单击 2 时,它会记录 2 次,单击 3 时,它会记录 3 次,单击 4 时,它会记录 4 次,依此类推。

这表明 Modal 实例在与 DOM 断开连接时不会被自动垃圾回收。我怀疑这是因为事件监听器。但是,侦听器函数本身不包含对 Modal 实例的任何引用(通过

this
除外)。

设置 Modal 的正确方法是什么,这样它就不会阻止自身被垃圾收集?

javascript html memory garbage-collection web-component
2个回答
2
投票

问题来自于你的

Modal
自定义元素,与GC无关。

您在

connectedCallback
中添加事件监听器,但永远不要删除它。 对话框不会离开 DOM,之前的自定义元素还没有被垃圾回收,因此它们的侦听器仍然处于活动状态。

如果你正确地断开了自定义元素与 DOM 的连接,你可以使用这个:

class Modal extends HTMLElement {
  constructor() {
    super();
    this.onDialogClose = this.onDialogClose.bind(this);
  }

  connectedCallback() {
    this.innerHTML = `<button onclick="this.closest('dialog').close()">Close modal</button>`;
    
    this.closest('dialog').addEventListener('close', this.onDialogClose);
  }

  disconnectedCallback() {
    this.closest('dialog').removeEventListener('close', this.onDialogClose);
  }

  onDialogClose() {
    console.log('Modal closed');
  }
}

customElements.define('app-modal', Modal);

0
投票

事件处理程序不会自动从跨打开/关闭周期停留的宿主元素(对话框)中删除。因此,即使从断开连接的自定义元素中,每个关闭事件都会调用重复的事件处理程序(侦听器仍保留)。

我认为可以用更好的方式完成,但是每当

addEventListener
中有
connectedCallback
时,
removeEventListener
中应该有相应的
disconnectedCallback
调用。

因此,来自

this.listener
的监听器(例如
this.host
)和主机引用(例如
connectedCallback
)必须以某种方式保留以便稍后释放。

请查看应用了修复程序的分叉演示:https://codepen.io/Ciunkos/pen/PogoRGV

© www.soinside.com 2019 - 2024. All rights reserved.