You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展多标签打开时service worker的onMessage.addListener触发两次问题

问题描述

当打开2个及以上扩展标签页时,chrome.runtime.sendMessage与chrome.runtime.onMessage.addListener会运行两次,仅单个标签页时功能正常。曾尝试用chrome.storage.local存储布尔值判断请求执行状态但无效,希望保留Service Worker以支持弹窗关闭后后台继续处理请求。


相关代码

Main.tsx

const generate = async (type: dalleReqTypes): Promise<void> => {
  try {
    await chrome.runtime
      .sendMessage({
        action: "dalle-fetch",
        type: type,
        request: {
          n: numToGen,
          prompt: prompt,
          image: selectedImg,
        },
      })
      .catch((err) => {
        console.log(err);
      });
  } catch (err) {
    console.log(err);
  }
};

Service_worker.ts

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === "dalle-fetch") {
    const dalleReq = message.request as DalleRequest;
        getDalleGenerations(dalleReq)
          .then(() => {
            sendResponse({ result: "success" });
          })
          .catch((error) => {
            sendResponse({ result: "error", dalleReq: error.toString() });
          });
});

Manifest.json

{
  "version": "0.0.1",
  "manifest_version": 3,
  "name": "App",
  "action": {
    "default_popup": "index.html",
    "default_title": "App",
    "default_icon": {
      "16": "images/camera.png",
      "48": "images/camera.png",
      "128": "images/camera.png"
    }
  },
  "background": { "service_worker": "service_worker.js", "type": "module" },
  "minimum_chrome_version": "92",
  "host_permissions": ["<all_urls>"],
  "permissions": ["contextMenus", "desktopCapture", "storage", "activeTab"],
  "icons": {
    "16": "images/camera.png",
    "48": "images/camera.png",
    "128": "images/camera.png"
  }
}

App.tsx

function App() {
  return (
    <HashRouter>
      <Suspense fallback={<div></div>}>
        <Routes>
          <Route index path="/" element={<Main isPopup={true} />} />
          <Route path="/browser" element={<Main isPopup={false} />} />
          <Route path="/cropper" element={<Cropper />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </Suspense>
    </HashRouter>
  );
}

问题成因
  1. Service Worker监听器重复注册:Manifest V3的Service Worker是事件驱动的,闲置时会被Chrome终止,新事件触发时会重启并重新执行整个脚本。如果监听器注册写在Service Worker顶层,每次重启都会重复添加监听器,导致单条消息被多个监听器处理。
  2. 消息广播到多上下文:chrome.runtime.sendMessage会向扩展所有活跃上下文(标签页、弹窗、Service Worker)广播消息,若标签页/弹窗中也注册了相同的onMessage监听器,每个上下文都会独立处理消息。
  3. 异步响应未保持通道:Service Worker的onMessage监听器中存在异步操作,但未返回true告知Chrome保持消息通道开放,可能引发异常触发重复执行。

修复方案

1. 避免Service Worker监听器重复注册

用标志变量控制监听器仅注册一次,同时补充异步响应的返回值:

// service_worker.ts
let listenerRegistered = false;

if (!listenerRegistered) {
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === "dalle-fetch") {
      const dalleReq = message.request as DalleRequest;
      getDalleGenerations(dalleReq)
        .then(() => sendResponse({ result: "success" }))
        .catch((error) => sendResponse({ result: "error", dalleReq: error.toString() }));
      // 返回true保持消息通道开放,支持异步响应
      return true;
    }
  });
  listenerRegistered = true;
}

2. 限制消息仅发送给Service Worker

直接向Service Worker发送消息,避免广播到其他上下文:

// Main.tsx
const generate = async (type: dalleReqTypes): Promise<void> => {
  try {
    const registration = await chrome.serviceWorker.getRegistration();
    if (registration?.active) {
      registration.active.postMessage({
        action: "dalle-fetch",
        type: type,
        request: {
          n: numToGen,
          prompt: prompt,
          image: selectedImg,
        },
      });
    }
  } catch (err) {
    console.log(err);
  }
};

同时修改Service Worker的消息监听方式:

// service_worker.ts
let listenerRegistered = false;

if (!listenerRegistered) {
  self.addEventListener('message', (event) => {
    const message = event.data;
    if (message.action === "dalle-fetch") {
      const dalleReq = message.request as DalleRequest;
      getDalleGenerations(dalleReq)
        .then(() => event.source?.postMessage({ result: "success" }))
        .catch((error) => event.source?.postMessage({ result: "error", dalleReq: error.toString() }));
    }
  });
  listenerRegistered = true;
}

3. 排查前端重复发送逻辑

检查前端代码是否存在多标签页场景下,每个标签页都触发generate函数的情况(如全局storage事件监听导致批量发送),添加条件判断仅让触发操作的标签页发送消息。


内容的提问来源于stack exchange,提问作者Sean

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 21:40:23