当弹出窗口重新打开时,为什么弹出脚本会向内容脚本发送更多消息?

Why the popup script send more message to content script, when the popup is reopened?

我有 firefox webextension。在 popup.html 中我有一个按钮,通过单击它,popup.js 将消息发送到 content.js。 Content.js 收到消息并用文本 "Message from popup" 制作 console.log。如果我再次单击该按钮,则会重复该操作。问题是当我离开弹出窗口并再次打开弹出窗口并再次单击按钮时,因为 content.js 从 popup.js 收到两条消息并发出两条 console.log。如果我再重复一次,content.js 收到三个消息,依此类推。 多少次我重新打开弹出窗口,多少次发送消息。

我认为问题出在 popup.js 的某处,但我想不通。

manifest.json:

{
    "manifest_version": 2,
    "name": "Extension",
    "version": "1.0",

    "description": "Firefox extension",

    "permissions": [
        "activeTab",
        "<all_urls>",
        "tabs"
    ],

    "browser_action": {
       "default_title": "Script",
       "default_popup": "popup.html"
    }
}

popup.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Title</title>
  </head>
  <body>
    <button class="action">Start</button>
    <script src="popup.js"></script>
  </body>
</html>

popup.js:

browser.tabs.executeScript({file: "content.js"})
.then(listenForClicks)

function listenForClicks() { 
  document.addEventListener("click", (e) => {
    if (e.target.classList.contains("action")) {
      browser.tabs.query({active: true, currentWindow: true})
        .then(send)
    }
    function send(tabs) { 
      browser.tabs.sendMessage(tabs[0].id, {
          command: "message"
          });
      }
  });
}

content.js:

function handleMessage(request, sender, sendResponse) {
    if(request.command === "message"){
      console.log("Message from popup");
    }
}

browser.runtime.onMessage.addListener(handleMessage);

预期的结果是点击按钮导致点击一次 console.log。甚至认为弹出窗口被重新打开了多次。

弹出窗口关闭后不会立即删除侦听器。垃圾收集器需要一些时间来删除它。

如果您使用的是 Firefox 50+,您可以在 EventTarget.addEventListener() 中为侦听器设置 once,以便在第一次后删除侦听器。

对于旧版浏览器,您可以使用 document.removeEventListener() 手动删除侦听器。

现在,如果您希望内容只接收一条消息,那么您可以在第一次后删除监听器,例如:

评论后更新

popup.js:

// add event listener for the button (not the whole pop-up), to run ONCE only
document.querySelector('button.action').addEventListener('click', listener, {once: true}); // FF50+, Ch55+

async function listener() {

  await browser.tabs.executeScript({file: 'content.js'});
  const tabs = await browser.tabs.query({currentWindow: true, active: true});
  browser.tabs.sendMessage(tabs[0].id, {command: 'message'});
}

content.js:

// add listener only if it wasnt done before
if (!sessionStorage.getItem('runOnce')) {

  // keep track of previous attempts in sessionStorage
  sessionStorage.setItem('runOnce', 'true');      
  browser.runtime.onMessage.addListener(handleMessage);
}

function handleMessage(request, sender, sendResponse) {

  if(request.command === 'message'){
    console.log("Message from popup");

    // remove listern after the first run
    browser.runtime.onMessage.removeListener(handleMessage);
  }
}