Chrome扩展Manifest V3迁移问题:多背景脚本与连接失败
解决Chrome扩展Manifest V3迁移的两个核心问题
一、合并多个背景脚本到单个Service Worker
Manifest V3仅支持单个service_worker文件,需将V2中的runtime.js合并到background.js中:
- 由于
background.js已设置type: "module",直接在文件顶部导入runtime.js:
import './runtime.js'; import {HttpClient, HttpErrorResponse, HttpXhrBackend} from "@angular/common/http"; // ... 原有代码
- 确认
runtime.js中的代码适配Service Worker环境(无DOM、window对象相关API调用)。
二、修复"Could not establish connection. Receiving end does not exist."错误
该错误由多个因素导致,逐一修复如下:
1. 恢复externally_connectable配置
V2中配置了该字段允许localhost页面与扩展通信,但V3的manifest缺失此配置,需添加:
修改V3的manifest.json,加入:
"externally_connectable": { "matches": [ "*://localhost/*" ] }
2. 替换localStorage为chrome.storage.local
Service Worker环境不支持localStorage(属于window对象),必须使用Chrome扩展存储API,修改background.js相关代码:
// 原代码: // const settings = JSON.parse(localStorage.getItem('settings') || '{}'); // 修改为异步获取: const result = await chrome.storage.local.get('settings'); const settings = result.settings || {};
同时需在manifest的permissions中添加"storage"权限。
3. 异步响应时返回true保持连接
当onMessageExternal中存在异步操作(如HttpClient请求),必须返回true告知Chrome等待异步完成后再调用sendResponse,否则连接会提前关闭:
chrome.runtime.onMessageExternal.addListener(async (request, sender, sendResponse) => { // ... 原有逻辑 // 返回true保持连接,等待异步响应 return true; });
4. 适配Service Worker生命周期
Service Worker是事件驱动的,闲置时会被销毁,确保代码通过事件(如onMessageExternal)触发激活,避免依赖持久化运行。
修改后的完整配置示例
manifest.json (V3)
{ "manifest_version": 3, "name": "...", "version": "1.1.7", "description": "...", "icons": { "128": "assets/logo128.png" }, "background": { "service_worker": "background.js", "type": "module" }, "permissions": [ "nativeMessaging", "storage" ], "action": { "default_popup": "index.html" }, "web_accessible_resources": [ { "resources": [ "assets/logo.png" ], "matches": [ "*://localhost/*" ] } ], "externally_connectable": { "matches": [ "*://localhost/*" ] } }
修改后的background.js
import './runtime.js'; import {HttpClient, HttpErrorResponse, HttpXhrBackend} from "@angular/common/http"; const http: HttpClient = new HttpClient(new HttpXhrBackend({build: (): XMLHttpRequest => new XMLHttpRequest()})); let host: string = 'http://localhost:8888'; console.log('start'); chrome.runtime.onMessageExternal.addListener(async (request, sender, sendResponse) => { console.log(request, sender, sendResponse); const result = await chrome.storage.local.get('settings'); const settings = result.settings || {}; if (settings?.server?.local?.host && settings.server.remote.host) { host = `http://${settings.server.local.host}:${settings.server.local.port}`; } console.log(settings, request, sender, sendResponse); const handleResponse = (err: HttpErrorResponse | null, data: any) => { request.body = err ? {error: err.error.error} : data; sendResponse(request.body); console.log(err ? err.error : data); }; if (request.message === 'certs') { http.get<any>(`${host}/api/sign/certificates`).subscribe(data => handleResponse(null, data), err => handleResponse(err, null)); } else if (request.message === 'sign') { http.post<any>(`${host}/api/sign/files`, request.body).subscribe(data => handleResponse(null, data), err => handleResponse(err, null)); } else if (request.message === 'certificate') { http.get<any>(`${host}/api/certificate?thumbprint=${request.body.thumbprint}`).subscribe(data => handleResponse(null, data), err => handleResponse(err, null)); } else if (request.message === 'version') { sendResponse({ type: 'success', version: chrome.runtime.getManifest().version }); } return true; });
内容的提问来源于stack exchange,提问作者Elvis Reis
相关产品推荐
相关产品推荐

