Chrome扩展内容脚本重复注入及消息通信异常求助
Chrome扩展重复注入Content Script及关闭停止问题解决
问题拆解
你遇到三个核心问题:
- 扩展加载/刷新时,现有标签页重复注入Content Script,导致DOM操作重复执行;
- 用
sendMessage判断是否已注入时,控制台一直报Could not establish connection错误; - 扩展关闭(禁用/卸载)时,Content Script未停止工作。
解决步骤
1. 修复Service Worker的事件监听
Manifest V3中,Service Worker不能用self.oninstall,必须用chrome.runtime.onInstalled,同时区分安装、更新、Chrome启动场景:
// background.js chrome.runtime.onInstalled.addListener(async (details) => { // 仅在安装或扩展更新时处理现有标签 if (details.reason === 'install' || details.reason === 'update') { const tabs = await chrome.tabs.query({url: "<all_urls>"}); for (const tab of tabs) { await injectIfNotExists(tab.id); } } }); // 扩展刷新(重新加载)时,Service Worker重启,补充处理现有标签 chrome.runtime.onStartup.addListener(async () => { const tabs = await chrome.tabs.query({url: "<all_urls>"}); for (const tab of tabs) { await injectIfNotExists(tab.id); } });
2. 安全判断是否需要注入(避免重复)
放弃用sendMessage判断(容易报错),改为执行一段小脚本检查标记:
// background.js 新增工具函数 async function injectIfNotExists(tabId) { try { // 检查当前标签是否已注入(用全局变量标记) const checkResult = await chrome.scripting.executeScript({ target: {tabId: tabId}, func: () => window._textHighlighterInjected === true, world: 'ISOLATED' // 和Content Script同一隔离环境 }); if (!checkResult[0].result) { // 并行注入JS和CSS,提升效率 await Promise.all([ chrome.scripting.executeScript({ target: {tabId: tabId}, files: ['content.js'] }), chrome.scripting.insertCSS({ target: {tabId: tabId}, files: ['styles.css'] }) ]); } } catch (error) { // 跳过无法注入的标签(如chrome://页面、about:blank) console.log('跳过标签:', tabId, error.message); } }
然后在Content Script里添加标记:
// content.js // 标记已注入,防止重复 window._textHighlighterInjected = true;
3. 解决sendMessage报错问题
如果一定要用消息通信,必须捕获chrome.runtime.lastError,避免控制台炸锅:
chrome.tabs.sendMessage(tabId, 'check-injected', (response) => { if (chrome.runtime.lastError) { // 通信失败,说明未注入 injectIfNotExists(tabId); return; } if (!response) injectIfNotExists(tabId); });
4. 扩展关闭时立即停止工作
在Content Script里监听chrome.runtime.onDisconnect事件,触发时清理所有资源:
// content.js // 监听扩展断开(禁用/卸载) chrome.runtime.onDisconnect.addListener(() => { cleanup(); }); // 清理函数,根据你的业务逻辑调整 function cleanup() { // 移除高亮元素 document.querySelectorAll('.highlight').forEach(el => el.remove()); // 移除事件监听 // document.removeEventListener('mouseup', yourHighlightFunc); // 清除定时器 // clearInterval(yourTimer); console.log('扩展已关闭,资源已清理'); }
完整代码
Manifest.json(无需修改)
{ "name": "Text highlighter", "description": "Text highlighter", "version": "1.0.0", "manifest_version": 3, "background": { "service_worker": "background.js" }, "options_ui": { "page": "options.html", "open_in_tab": false }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"], "css": [ "styles.css" ] } ], "permissions": ["storage", "activeTab", "scripting", "tabs"], "host_permissions": ["<all_urls>"] }
background.js
chrome.runtime.onInstalled.addListener(async (details) => { if (details.reason === 'install' || details.reason === 'update') { const tabs = await chrome.tabs.query({url: "<all_urls>"}); for (const tab of tabs) { await injectIfNotExists(tab.id); } } }); chrome.runtime.onStartup.addListener(async () => { const tabs = await chrome.tabs.query({url: "<all_urls>"}); for (const tab of tabs) { await injectIfNotExists(tab.id); } }); async function injectIfNotExists(tabId) { try { const checkResult = await chrome.scripting.executeScript({ target: {tabId: tabId}, func: () => window._textHighlighterInjected === true, world: 'ISOLATED' }); if (!checkResult[0].result) { await Promise.all([ chrome.scripting.executeScript({ target: {tabId: tabId}, files: ['content.js'] }), chrome.scripting.insertCSS({ target: {tabId: tabId}, files: ['styles.css'] }) ]); } } catch (error) { console.log('跳过无法注入的标签:', tabId, error.message); } }
content.js
window._textHighlighterInjected = true; chrome.runtime.onDisconnect.addListener(() => { cleanup(); }); chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { console.log('收到消息:', request); sendResponse(true); return true; }); function cleanup() { document.querySelectorAll('.highlight').forEach(el => el.remove()); // 这里添加你的其他清理逻辑 console.log('扩展已停止,资源已清理'); }
内容的提问来源于stack exchange,提问作者webdigga
相关产品推荐
相关产品推荐

