Vite开发模式下Chrome扩展sendMessage报无法建立连接错误求助
解决Vite开发模式下Chrome扩展新标签页sendMessage竞态问题
在开发Chrome扩展时,遇到Vite开发模式下新标签页加载后chrome.tabs.sendMessage报错Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.的问题。生产环境使用Rollup构建静态HTML时正常,但Vite开发模式下页面动态加载导致脚本未就绪,必须用setTimeout延迟发送才能解决,以下是三种可靠的优化方案:
方案1:基于消息双向确认的可靠通信
核心思路是让新标签页的脚本加载完成后主动通知Background,Background收到通知后再发送目标数据,彻底避免竞态问题。
Background端代码修改
export function launchSvgGobbler() { // 存储待发送的标签页数据 let pendingTabTask = null; // 监听新标签页的就绪通知 chrome.runtime.onMessage.addListener((message, sender) => { if (message.type === 'TAB_READY' && pendingTabTask?.tabId === sender.tab.id) { // 标签页就绪,发送业务数据 chrome.tabs.sendMessage(sender.tab.id!, pendingTabTask.data); pendingTabTask = null; } }); chrome.action.onClicked.addListener(async ({ url }) => { try { const tab = await utils.createNewTab(); // 暂存数据和标签ID,等待就绪通知 pendingTabTask = { tabId: tab.id!, data: { data: 'data' } }; } catch (error) { console.log('Error launching', error); pendingTabTask = null; } }); }
新标签页脚本修改
在新标签页的入口脚本中,页面初始化完成后发送就绪通知,并监听数据消息:
// 新标签页入口文件(如 src/pages/newTab/index.js) window.addEventListener('DOMContentLoaded', () => { // 通知Background:当前标签页脚本已就绪 chrome.runtime.sendMessage({ type: 'TAB_READY' }); }); // 监听Background发来的业务数据 chrome.runtime.onMessage.addListener((message) => { if (message.data) { // 处理收到的数据 console.log('Received data:', message.data); } });
方案2:优化标签页加载完成的判断逻辑
通过在createNewTab中增加测试消息的重试逻辑,确保标签页脚本真正就绪后再返回Promise。
修改createNewTab函数
/** * Awaits the loading of a newly created tab and return the tab config. * @param url The url to open relative to the extension. Defaults to index.html. */ export function createNewTab(url = 'index.html'): Promise<chrome.tabs.Tab> { return new Promise((resolve) => { chrome.tabs.create({ url, active: true }, (tab) => { const listener = async (updatedTabId, changeInfo, updatedTab) => { if ( tab.id && updatedTabId === tab.id && changeInfo.status === 'complete' && updatedTab.status === 'complete' ) { if (process.env.NODE_ENV === 'development') { try { // 发送测试消息,验证脚本是否就绪 await chrome.tabs.sendMessage(tab.id!, { type: 'PING' }); // 验证成功,移除监听并返回标签页 chrome.tabs.onUpdated.removeListener(listener); resolve(updatedTab); } catch (e) { // 验证失败,继续等待(下一次onUpdated触发时重试) return; } } else { chrome.tabs.onUpdated.removeListener(listener); resolve(updatedTab); } } }; chrome.tabs.onUpdated.addListener(listener); }); }); }
新标签页脚本增加PING响应
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { // 响应测试消息 if (message.type === 'PING') { sendResponse('PONG'); } // 其他业务消息处理... });
方案3:让Vite开发模式生成静态HTML
配置Vite在开发模式下也生成静态index.html文件,模拟生产环境的加载方式:
// vite.config.js import { defineConfig } from 'vite'; import copy from 'vite-plugin-static-copy'; export default defineConfig({ plugins: [ process.env.NODE_ENV === 'development' && copy({ targets: [ { src: 'index.html', dest: 'dist' } ] }) ].filter(Boolean), // 确保开发服务器的资源路径和扩展匹配 server: { port: 5173, strictPort: true } });
之后修改createNewTab的默认URL指向生成的静态文件路径,同时调整扩展manifest中的权限和资源路径配置。
内容的提问来源于stack exchange,提问作者Ross Moody
相关产品推荐
相关产品推荐

