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

Manifest V3 Chrome扩展后台脚本消息传递报错解决方案

错误原因分析

你遇到的Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist错误由三个核心问题直接导致:

  • chrome.tabs.create是异步API,调用后浏览器需要完成标签页创建、HTML加载、JS执行全流程,你在调用创建接口后立刻执行发消息逻辑,此时popup.js的消息监听器还未完成注册,自然找不到接收端。
  • 现有逻辑没有判断popup.html是否已经打开,每次收到content script的消息都会新建一个标签页,哪怕你提前打开了popup页面,代码还是会创建新的未加载完成的标签页,第一次执行必然报错。
  • chrome.runtime.sendMessage默认会向所有扩展上下文广播消息,只要有一个上下文不存在可用接收端就会抛出Promise异常,你没有添加异常捕获逻辑,错误直接打到控制台。
  • 额外隐患:MV3的background service worker是事件驱动、随时可能被回收的,把待发送数据存在内存里可能出现数据丢失。
可直接运行的修复代码

修正后的background.js

// 缓存待传递数据,避免页面加载慢导致丢数
let pendingSelectedText = null;

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  // 处理content script发来的选中文本
  if (request.type === 'selectedText') {
    pendingSelectedText = request.data;
    // 先查询popup页面是否已经打开
    chrome.tabs.query({url: chrome.runtime.getURL('popup.html')}, (tabs) => {
      if (tabs.length > 0) {
        // 已存在则直接向对应标签页发消息,加catch避免无接收端时报错
        chrome.tabs.sendMessage(tabs[0].id, {
          type: 'bgForward',
          data: pendingSelectedText
        }).catch(err => console.log('消息发送异常:', err));
      } else {
        // 不存在则新建标签页,等加载完成后再发消息
        chrome.tabs.create({url: 'popup.html'}, (newTab) => {
          const tabLoadListener = (tabId, changeInfo) => {
            if (tabId === newTab.id && changeInfo.status === 'complete') {
              chrome.tabs.sendMessage(newTab.id, {
                type: 'bgForward',
                data: pendingSelectedText
              }).catch(err => console.log('消息发送异常:', err));
              // 移除监听器避免内存泄漏
              chrome.tabs.onUpdated.removeListener(tabLoadListener);
            }
          };
          chrome.tabs.onUpdated.addListener(tabLoadListener);
        });
      }
    });
    return true;
  }

  // 处理popup页面初始化时拉取缓存数据的请求
  if (request.type === 'getPendingData') {
    sendResponse({data: pendingSelectedText});
    // 按需决定是否清空缓存
    // pendingSelectedText = null;
    return true;
  }
});

修正后的content.js

// 避免隐式全局变量
function getSelectionText() {
  let text = "";
  if (window.getSelection) {
    text = window.getSelection().toString();
  } else if (document.selection && document.selection.type != "Control") {
    text = document.selection.createRange().text;
  }
  return text;    
}

function doc_keyUp(e) {
  const selectedText = getSelectionText();
  if (selectedText && e.key === '1') {
    chrome.runtime.sendMessage({
      type: 'selectedText',
      data: selectedText
    }).catch(err => console.log('向后台发送数据异常:', err));
  }
}

console.log('The program has started!!!');
document.addEventListener('keyup', doc_keyUp, false);

修正后的popup.js

// 监听后台实时转发的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.type === 'bgForward') {
    console.log("from popup = \n" + request.data);
  }
});

// 页面加载完成后主动拉取缓存数据,避免加载过慢错过实时消息
chrome.runtime.sendMessage({type: 'getPendingData'}, (res) => {
  if (res?.data) {
    console.log("初始化拿到缓存数据 = \n" + res.data);
  }
});

popup.html和manifest.json无需额外修改,保持原有结构即可正常运行。

附加问题解答

1. 能否跳过background直接从content script发消息到自定义HTML页面脚本?

没有直接通信的通道。content script运行在目标网页的隔离上下文,和扩展自有HTML页面(popup页、选项页、自定义标签页)的上下文完全隔离,不存在直连的通信端口。
如果不想写background转发逻辑,可以用chrome.storage做中转实现类似效果:

  • content script拿到选中文本后直接写入chrome.storage.local
  • 自定义HTML页面监听chrome.storage.onChanged事件,数据更新时直接读取即可
    这种方案不需要等页面提前打开,数据会持久化在存储中,页面打开后也能主动读取历史数据,也不会出现「接收端不存在」的报错。

2. MV3下能否不通过消息传递直接获取background内的变量?

没有直接获取的方法。MV3将后台上下文替换为Service Worker,和扩展页面运行在不同线程,且生命周期完全独立(随时可能被浏览器回收销毁),内存数据不共享,因此chrome.extension.getBackgroundPage()被彻底移除,没有等价API可以直接访问Service Worker的内存变量。
可行的替代方案只有两种:

  • 将需要全局共享的变量写入chrome.storage持久化,所有上下文直接读写存储即可
  • 保持消息传递逻辑:其他页面向background发请求,background将变量值通过响应返回
    注意不要把需要长期保留的数据存在Service Worker的内存中,Service Worker被回收时内存数据会直接丢失。

内容的提问来源于stack exchange,提问作者Consider Non-Trivial Cases

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 10:09:20