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

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"
    }
}
<script type="module" src="popup.js"></script>

<div class="container">
    <div class="title">Plex File Name Generator</div>
</div>
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 04:25:02