首页 > 解决方案 > 当弹出窗口重新打开时,为什么弹出脚本会向内容脚本发送更多消息?

问题描述

我有 Firefox 网络扩展。在 popup.html 我有一个按钮,通过单击它,popup.js 将消息发送到 content.js。Content.js 接收消息并使用文本“来自弹出窗口的消息”制作 console.log。如果我再次单击该按钮,则会重复该操作。问题是当我单击远离弹出窗口并再次打开弹出窗口并再次单击按钮时,因为 content.js 从 popup.js 收到两条消息并生成两个 console.log。如果我再重复一遍,content.js 会收到三条消息,依此类推。我重新打开弹出窗口多少次,与发送消息的次数一样多。

我认为问题出在 popup.js 中,但我无法弄清楚。

清单.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"
          });
      }
  });
}

内容.js:

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

browser.runtime.onMessage.addListener(handleMessage);

预期结果是单击按钮导致生成一个console.log。甚至认为弹出窗口已多次重新打开。

标签: firefox-addonfirefox-addon-webextensions

解决方案


弹出窗口关闭后不会立即删除侦听器。垃圾收集器需要一些时间才能将其移除。

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

对于较旧的浏览器,您可以使用 手动删除侦听器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'});
}

内容.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);
  }
}

推荐阅读