Chrome扩展开发:如何在弹窗关闭时从内容脚本向其稳定发消息?
解决Chrome扩展弹窗关闭后内容脚本无法发送消息的问题
问题根源
当弹窗(popup)关闭时,它的JavaScript上下文会被Chrome销毁,对应的chrome.runtime.onMessage监听器也会失效。此时内容脚本(content.js)调用chrome.runtime.sendMessage找不到接收端,就会触发Could not establish connection. Receiving end does not exist.错误。
可行解决方案
方案1:通过后台Service Worker中转消息
利用Chrome扩展的后台Service Worker作为持久化的消息中转层,内容脚本先把标题发送到后台存储,弹窗打开时再从后台获取数据。
步骤1:配置manifest.json
确保已声明后台Service Worker:
{ "manifest_version": 3, "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": ["*://*.youtube.com/watch*"], "js": ["content.js"] } ], "action": { "default_popup": "popup.html" } }
步骤2:修改content.js
将消息发送给后台Service Worker:
window.onload = () => { const videoTitle = document.querySelector("#title h1 yt-formatted-string")?.textContent; if (videoTitle) { chrome.runtime.sendMessage({ type: "VIDEO_NAME", value: videoTitle }); } };
步骤3:新增background.js
监听内容脚本的消息并存储视频标题,同时提供弹窗获取数据的接口:
let currentVideoTitle = ""; // 接收内容脚本的标题消息 chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === "VIDEO_NAME") { currentVideoTitle = request.value; } }); // 响应弹窗的标题请求 chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === "GET_VIDEO_NAME") { sendResponse(currentVideoTitle); } });
步骤4:修改popup.js
弹窗打开时主动从后台获取存储的标题:
document.addEventListener('DOMContentLoaded', async () => { const response = await chrome.runtime.sendMessage({ type: "GET_VIDEO_NAME" }); if (response) { // 假设弹窗内有id为title-display的元素用于展示标题 document.getElementById("title-display").textContent = response; } });
方案2:直接用chrome.storage存储标题
跳过后台脚本,内容脚本直接把标题存入Chrome本地存储,弹窗打开时读取存储的数据。
修改content.js
window.onload = () => { const videoTitle = document.querySelector("#title h1 yt-formatted-string")?.textContent; if (videoTitle) { chrome.storage.local.set({ currentVideoTitle: videoTitle }); } };
修改popup.js
document.addEventListener('DOMContentLoaded', async () => { const result = await chrome.storage.local.get("currentVideoTitle"); if (result.currentVideoTitle) { document.getElementById("title-display").textContent = result.currentVideoTitle; } });
方案对比
- 方案1:适合需要在后台处理额外逻辑(如标题去重、格式过滤)的场景,但需要维护后台Service Worker的状态。
- 方案2:代码更简洁,依赖Chrome原生存储API,适合简单的数据传递需求。
内容的提问来源于stack exchange,提问作者Adnan Arif Sait
相关产品推荐
相关产品推荐

