跳转到主要内容
Manifest V3(MV3)用 service worker 取代了背景页,收紧了内容安全策略,并重塑了扩展声明网络和宿主访问的方式。大多数日常痛点都有同一个根因:MV3 假设背景代码是短暂的、事件驱动的。Chrome 和 Firefox 对少数几个键的解释也有所不同。 本页汇总了构建 MV3 扩展时最常出现的问题,给出精确修复方案以及 Extension.js 会替你处理的部分。

Manifest V3 中带 type: "module" 的背景 service_worker

Chromium 在 MV3 背景中使用 service worker。要从中导入 ES 模块,manifest 需要 type: "module"
{
  "manifest_version": 3,
  "background": {
    "service_worker": "service_worker.js",
    "type": "module"
  }
}
如果没有 "type": "module"import 语句会在注册时失败,并且扩展控制台里看不到清晰的报错。加上之后,你就可以在 worker 文件里使用现代 ES 模块语法。 当你的背景入口使用 ES 模块语法时,Extension.js 会自动设置 type: "module"。它还会把 .ts/.tsx worker 编译成单个打包产物,让模块解析在生产环境中仍然可用。

为什么 background.js 在 Manifest V3 中表现不同

在 Manifest V2 中,background.js 运行在一个持久的、带 DOM 的背景页里。在 Chromium 的 Manifest V3 中,背景作为 service worker 运行:
  • 没有 DOM。windowdocumentXMLHttpRequestlocalStorage 都没有了。请使用 fetchchrome.storage
  • worker 在空闲时 可能随时被终止,并在下一个事件到来时被唤醒。不要把状态存在模块作用域变量中;用 chrome.storage 持久化。
  • 允许顶层 await,但长时间的初始化本身不会让 worker 一直存活。
  • 在文件顶部 同步地 注册事件监听器,不要在异步回调内部注册。在异步回调内部注册的监听器,对于唤醒并加载这个 worker 的那个事件不会触发。
Firefox 仍然使用非持久事件页,而不是 service worker。Extension.js 会从同一份源代码把 Chromium 路由到 service_worker,把 Firefox 路由到 scripts,从而让同一个背景入口为每个目标正确编译。

Manifest V3 中的 web_accessible_resources

Manifest V3 把 web_accessible_resources 从一个扁平的文件数组改为 { resources, matches } 块的列表:
{
  "web_accessible_resources": [
    {
      "resources": ["images/logo.png", "pages/injected.html"],
      "matches": ["https://example.com/*"]
    }
  ]
}
常见错误:
  • 列出了一个不在构建产物中的路径。Extension.js 只输出被 manifest.json、入口 HTML 或导入代码引用的文件。请把文件作为资源添加进去,它才会落到 dist/<browser>
  • 忘了 matches。没有它,资源不会暴露给任何源。
  • 使用 V2 风格的扁平字符串。浏览器在 MV3 中会默默忽略它们。
完整模式见 web_accessible_resources 实现,其中包含把一个扩展 URL 注入 content script 的做法。

host_permissionspermissions

Manifest V3 把宿主访问从 permissions 数组中拆了出来:
控制内容
permissions命名 API 接口(storagetabscookiesscriptingalarms 等)。
host_permissions扩展可以读取或修改的 URL 匹配模式(https://*/**://api.example.com/*)。
optional_permissionsoptional_host_permissions在运行时通过 chrome.permissions.request 请求的权限。
混淆它们的常见症状:
  • chrome.cookies.get 在你有访问权限的站点上返回空:该 URL 需要 host_permissions,不只是 cookies
  • chrome.scripting.executeScript 报错 “Cannot access contents of url”:把该 URL 加入 host_permissions
  • Web Store 向用户警告”全站访问”,而你其实只需要一个源:把宿主模式收窄。
按 API 的参考见 权限与宿主权限

Firefox 中的 declarative_net_request

declarative_net_request(DNR)是 MV3 中用于替代阻断式 webRequest 的方案。Firefox 支持它,但有几条限制:
  • 静态规则资源文件(rule_resources)必须是合法的 JSON 数组。相比 Chrome,Firefox 对空文件或格式不正确的规则文件更严格。
  • Chrome 支持的一些规则动作和条件在 Firefox 中仍是部分支持。请查阅 MDN 的 declarativeNetRequest 参考 了解当前的兼容矩阵。
  • 要让 DNR 规则在跨源请求上生效,需要 host_permissions(或 <all_urls>)。
Extension.js 在构建时校验 DNR 资源,并按浏览器输出产物,让某个 Firefox 专属的规则文件不会落到 Chrome 构建里。

Content script、worker 与扩展 URL

MV3 容易绊倒人的三个地方:
  • Content script 无法访问页面的 JavaScript 作用域。 它们共享 DOM,而不是 window。请使用 window.postMessage(或一个指向 web_accessible_resources 条目的 <script> 标签)来与页面代码通信。
  • Service worker 在安装期间无法可靠地访问 chrome.runtime.getURL 请在需要它的事件处理器内部惰性地解析 URL。
  • 扩展 URL 是按源限定的。 你的扩展中的两个页面可以互相通信,但页面无法 fetch 它们,除非它们被列在 web_accessible_resources 中。

Extension.js 在 Manifest V3 中如何帮你

  • 自动把背景入口路由到 service_worker(Chromium)或 scripts(Firefox)。
  • 当背景使用 ES 模块语法时,自动设置 type: "module"
  • 在构建时根据当前 manifest 版本校验 web_accessible_resourcespermissionshost_permissions
  • 按浏览器输出 dist/<browser> 产物,让 Chrome 和 Firefox 之间的 MV3 差异彼此隔离。
  • extension dev 期间,保存即重载 content script、service worker 和 HTML 页面。

下一步