Chrome扩展ManifestV3:后台向内容脚本发消息报错连接失败
已解决:Chrome扩展跨文件数据传递问题
问题背景
正在学习Chrome扩展中不同文件间的数据传递方法,目标是当特定URL加载时,从网站DOM中查询数据并在扩展弹窗中展示。目前在内容脚本、弹窗脚本与后台脚本的消息事件顺序及数据传递方面遇到问题。
预期流程
- 后台脚本通过URL比对判断当前标签页是否为目标站点
- 后台脚本通过
chrome.tabs.sendMessage向内容脚本发送消息 - 内容脚本通过
chrome.runtime.onMessage.addListener()接收消息,并从网站DOM中收集信息 - 内容脚本通过
chrome.runtime.sendMessage将结果发送给弹窗脚本,填充popup.html中的信息
错误现象
可通过检查弹窗看到background.js的console.log,但出现未捕获的Promise错误:
Error: Could not establish connection. Receiving end does not exist.,且网页或弹窗开发者工具控制台中没有后续的console.log输出。
文件结构
root/ ├─ manifest.json ├─ background.js ├─ popup/ │ ├─ popup.html │ ├─ popup.js └─ scripts/ └─ content.js
代码文件
manifest.json
{ "manifest_version": 3, "name": "Plex File Name Generator", "version": "0.0.1", "description": "Generate file name for Plex from IMDb", "permissions": ["tabs"], "host_permissions": ["https://*.imdb.com/tt*"], "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": ["https://*.imdb.com/tt*"], "js": ["scripts/content.js"] } ], "action": { "default_icon": { "16": "assets/ext-icon.png", "24": "assets/ext-icon.png", "32": "assets/ext-icon.png" }, "default_title": "Plex File Name Generator", "default_popup": "popup/popup.html" } }
popup.html
<script type="module" src="popup.js"></script> <div class="container"> <div class="title">Plex File Name Generator</div> </div>
popup.js
chrome.runtime.onMessage.addListener((obj, sender, response) => { const { type, movieTitle, imdbId } = obj; if (type === "displayMovieTitle") { console.log("popup", movieTitle, imdbId); } });
content.js
(() => { chrome.runtime.onMessage.addListener((request, sender, response) => { const { type, imdbId } = request; if (type === "getMovieInfo") { const titleSpan = document.querySelector( 'h1[data-testid="hero__pageTitle"] > span' ); const movieTitle = titleSpan?.innerText?.trim() || ""; chrome.runtime.sendMessage({ type: "displayMovieTitle", movieTitle, imdbId }); } return true; }); })();
background.js
chrome.tabs.onUpdated.addListener((tabId, tab) => { if (tab.url?.includes("imdb.com/title/tt")) { chrome.tabs.onUpdated.addListener((nextTabId, nextTab) => { if (tabId === nextTabId && nextTab.status === "complete") { const imdbId = tab.url.match(/tt\d+/g)?.[0] || ""; chrome.tabs.sendMessage(tabId, { type: "getMovieInfo", imdbId, }); } }); } });
问题分析与修复
核心问题1:后台脚本重复绑定监听器
当前background.js在chrome.tabs.onUpdated回调里嵌套绑定了另一个同类型监听器,会导致每次标签页更新时新增监听器,多次触发消息发送,甚至在内容脚本未完成注入时就发消息,引发“接收端不存在”的错误。
核心问题2:消息传递路径错误
内容脚本用chrome.runtime.sendMessage直接发消息给弹窗,但弹窗仅在打开时存在,且该API默认是向后台脚本发送消息,无法直接触达弹窗。
修复方案(推荐弹窗主动请求模式)
因为弹窗仅在用户点击扩展图标时打开,此时主动向当前标签页的内容脚本请求数据更合理,避免后台盲目发送消息:
修改popup.js
document.addEventListener('DOMContentLoaded', async () => { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (tab.url?.includes("imdb.com/title/tt")) { try { const { movieTitle, imdbId } = await chrome.tabs.sendMessage(tab.id, { type: "getMovieInfo" }); console.log("popup", movieTitle, imdbId); // 将数据渲染到弹窗DOM document.querySelector('.title').textContent = movieTitle || "未获取到标题"; } catch (err) { console.error("获取数据失败:", err); } } });
修改content.js
(() => { chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === "getMovieInfo") { const titleSpan = document.querySelector('h1[data-testid="hero__pageTitle"] > span'); const movieTitle = titleSpan?.innerText?.trim() || ""; const imdbId = window.location.href.match(/tt\d+/g)?.[0] || ""; sendResponse({ movieTitle, imdbId }); } return true; }); })();
简化background.js
可移除原有的消息发送逻辑,因为弹窗会主动发起请求:
// 若无需其他后台逻辑,可留空或仅保留必要代码
额外注意事项
- Manifest V3中后台脚本是Service Worker,生命周期短,不适合存储临时数据,优先让弹窗与内容脚本直接通信
- 使用
chrome.tabs.sendMessage时需添加catch捕获错误,避免未处理的Promise异常 - 确保内容脚本的
matches规则正确匹配目标站点,保证脚本成功注入
内容的提问来源于stack exchange,提问作者GreenSnow02
相关产品推荐
相关产品推荐

