Chrome扩展Manifest V3:已断开端口对象错误的优雅解决方法
问题
开发基于Manifest V3的Chrome扩展时,content script通过port与service worker建立连接通信,正常情况运行良好,但service worker因30秒无活动被终止后,content script再通过该port发送消息会抛出错误:
Attempting to use a disconnected port object
导致service worker无法处理消息。
简化后的代码示例:
Content Script代码
const port = chrome.runtime.connect({ name: 'myport' }); // 包裹在点击监听器中 port.postMessage({ foo: 'bar' });
Service Worker代码
chrome.runtime.onConnect.addListener((port) => { if (port.name === 'myport') { port.onMessage.addListener((m) => { setTimeout(() => { port.postMessage({ id: 'back' }); }, 1000); }); } });
已尝试用单消息替代port通信,仍遇到类似错误。不想通过让service worker持续活跃的方式解决,希望找到原生优雅方案,确保service worker终止后通信仍能保持健壮性。
解决方案
1. 监听port断开事件,自动重连+消息队列
在content script中监听port的onDisconnect事件,检测到连接断开时自动重建port,同时用队列缓存待发送的消息,重连成功后批量发送。
修改后的Content Script代码:
let port; const messageQueue = []; function createPort() { port = chrome.runtime.connect({ name: 'myport' }); port.onDisconnect.addListener(() => { setTimeout(createPort, 1000); }); port.onMessage.addListener((msg) => { console.log('收到返回消息:', msg); }); // 发送队列积压的消息 while (messageQueue.length > 0) { port.postMessage(messageQueue.shift()); } } // 初始化连接 createPort(); // 封装消息发送逻辑 function sendMessage(msg) { if (port?.sender) { port.postMessage(msg); } else { messageQueue.push(msg); } } // 点击监听器中调用:sendMessage({ foo: 'bar' });
2. 单消息通信+超时重试
针对chrome.runtime.sendMessage的类似问题,添加重试逻辑,同时在service worker中返回true确保异步处理时worker不会被提前终止。
Content Script代码:
function sendMessageWithRetry(msg, retries = 3, delay = 1000) { return new Promise((resolve, reject) => { function attempt(retryCount) { chrome.runtime.sendMessage(msg, (response) => { if (chrome.runtime.lastError) { retryCount > 0 ? setTimeout(() => attempt(retryCount - 1), delay) : reject(chrome.runtime.lastError); } else { resolve(response); } }); } attempt(retries); }); } // 使用示例: // sendMessageWithRetry({ foo: 'bar' }) // .then(res => console.log('收到响应:', res)) // .catch(err => console.error('发送失败:', err));
Service Worker代码:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg.foo === 'bar') { setTimeout(() => { sendResponse({ id: 'back' }); }, 1000); // 返回true告知Chrome保持worker活跃直到回调执行 return true; } });
3. 本地存储暂存消息(兜底方案)
如果service worker终止时消息未被处理,可将消息暂存到chrome.storage.local,worker启动时读取并处理。
Content Script代码:
async function sendMessageWithBackup(msg) { try { await chrome.runtime.sendMessage(msg); } catch (err) { const { pendingMessages } = await chrome.storage.local.get('pendingMessages'); await chrome.storage.local.set({ pendingMessages: [...(pendingMessages || []), msg] }); } }
Service Worker代码:
chrome.runtime.onStartup.addListener(async () => { const { pendingMessages } = await chrome.storage.local.get('pendingMessages'); if (pendingMessages?.length) { pendingMessages.forEach(msg => { // 执行消息处理逻辑 console.log('处理暂存消息:', msg); }); await chrome.storage.local.set({ pendingMessages: [] }); } });
内容的提问来源于stack exchange,提问作者Js1234
相关产品推荐
相关产品推荐

