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

Chrome扩展Manifest V3迁移问题:多背景脚本与连接失败

解决Chrome扩展Manifest V3迁移的两个核心问题

一、合并多个背景脚本到单个Service Worker

Manifest V3仅支持单个service_worker文件,需将V2中的runtime.js合并到background.js中:

  1. 由于background.js已设置type: "module",直接在文件顶部导入runtime.js:
import './runtime.js';
import {HttpClient, HttpErrorResponse, HttpXhrBackend} from "@angular/common/http";
// ... 原有代码
  1. 确认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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 01:55:22