为什么重要
浏览器之间在 manifest 的几个关键区域仍有差异,比如后台脚本配置和厂商元数据。带前缀的字段让你能保留一份源manifest.json,同时为 Chromium 系和 Firefox 系目标生成各自正确的产物。
工作原理
Extension.js 扫描 manifest 的键,并按所选浏览器解析带前缀的条目。前缀要么指定一个引擎家族,要么指定单个浏览器:chromium:覆盖所有 Chromium 系目标(chromium、chrome、edge、chromium-based,分支brave、opera、vivaldi、yandex,以及 Safari 产物)chrome:只覆盖chrome,edge:只覆盖edgefirefox:与gecko:覆盖所有 Gecko 系目标(firefox、gecko-based,以及分支waterfox、librewolf)
brave 或 waterfox 这样的分支时,一份只带 chromium:/firefox: 键的 manifest 仍能被正确解析。精确的浏览器名称前缀也会匹配它自己的目标(例如运行 --browser=brave 时的 brave:)。
Chromium 系浏览器(Chrome、Edge 等)
Firefox
service_worker 只会出现在 Chromium 系产物里,而 Firefox 产物里则保留 background.scripts。
支持的前缀映射:
精确的浏览器名称前缀(例如
chrome:、edge:、brave: 或 waterfox:)只有在你面向同一个浏览器时才会解析。它胜过所属的家族前缀,因此为 chrome 构建时 chrome: 胜过 chromium:。
Safari 产物继承 Chromium 家族,因为转换器消费的是 Chrome 形态的 manifest。chromium: 键适用于 Safari,但 chrome: 和 edge: 键不适用。只想覆盖 Safari 时请使用 safari:(或 webkit:),它们优先于 chromium: 键。
Safari 没有实现的键与权限会从 Safari 构建里自动移除,并且从 4.1.20 起,构建会为每个被移除的键打印一行,说明是哪个键、为什么被移除。content_scripts[].world 会被保留,因为从 Safari 18 起 Safari 就支持它。详见 构建 Safari 扩展。
它适用于 manifest 中任意层级的任何字段,包括 permissions、content_scripts 和 background。
只面向一个 Chromium 厂商
chromium: 是家族前缀。chrome: 和 edge: 各自只指定一个浏览器,因此一个字段可以只发布到一个商店,而不进入另一个商店。例如,Chrome Web Store 的 key 不能进入 Edge Add-ons 的包:
extension build --browser=chrome 会输出 key。extension build --browser=edge 不会包含它。
不要写 edge:key。Edge Add-ons 会拒绝任何在 manifest 中包含 key 的包,因此该字段在 Edge 构建里没有用处。扩展 id 由 Partner Center 分配。从 4.1.20 起,生产模式的 Edge 构建会丢弃 key 并打印一行说明原因,开发模式的构建会保留它,因为此时稳定的 id 有用,而且不涉及任何商店。
前缀匹配的是你用 --browser 请求的浏览器,而不是实际启动的二进制。当 Extension.js 退回到另一个浏览器二进制时,前缀仍按请求的目标解析。
从 Extension.js 4.1.19 起,
chrome: 和 edge: 成为精确的浏览器前缀。在 4.1.18 及更早版本中,它们适用于每一个 Chromium 系目标。如果构建丢弃了 4.1.18 会应用的 chrome: 或 edge: 键,构建会打印一条点名该键的警告。把该键重命名为 chromium: 即可保留原来的覆盖范围。优先级:三层结构
当多个键设置同一个字段时,胜出者由层级决定,而不是由它在文件中的位置决定:- 不带前缀的普通键是基础。
- 家族前缀(Chromium 目标上的
chromium:,Gecko 目标上的firefox:与gecko:)覆盖普通键。 - 精确前缀覆盖前两者。精确指的是该前缀点名了确切的目标,比如为
chrome构建时的chrome:,或为brave构建时的brave:。在 Safari 和 webkit 系目标上,safari:和webkit:都属于精确前缀。
chrome 构建会输出 "Chrome"。chrome: 点名了确切的目标,因此位于精确层,胜过 chromium:。为 edge、chromium 或 brave 构建则输出 "Family",因为 chrome: 不适用于这些目标。
只有同一层级里有两个前缀时才会平局,比如 waterfox 这样的 Gecko 分支上的 firefox: 与 gecko:,或 Safari 目标上的 safari: 与 webkit:。源码顺序靠后的键胜出。
只要带前缀的键匹配,它总是覆盖同名的普通键,无论两者在文件中的先后位置。
前缀在任意层级都会解析
解析器会遍历整棵 manifest 树,包括数组。content_scripts 条目内部或任意嵌套对象内部的带前缀键,都按与顶层键相同的三层规则解析。
同一个解析器也驱动入口发现
前缀解析不只作用于输出的 JSON。同一个解析器会在 script 和 HTML 入口发现之前运行,因此firefox:background 脚本或带前缀的页面只有在匹配的目标上才会成为被编译的入口。
分支与 *-based 别名
家族分类先匹配一份已知分支清单,再退回到子串检查。chrome、edge、brave、opera、vivaldi 和 yandex 按名字归类为 Chromium 系,任何其他包含 chromium 的名字也一样。firefox、waterfox 和 librewolf 按名字归类为 Gecko 系,任何其他包含 gecko 或 firefox 的名字也一样。这就是 chromium-based 和 gecko-based 别名,以及基于它们构造的任意 *-based 名字,都能继承所属家族带前缀键的原因。
带前缀的 manifest_version 需要一个不带前缀的兜底值
chrome: 和 edge: 是精确前缀,所以只作用于某一个厂商的 manifest_version 会让其他所有构建都没有这个字段。下面这份 manifest 给了 Firefox 和 Chrome 一个版本号,却没有给 Edge:
manifest_version 的 manifest 任何浏览器都不会加载。从 4.1.21 开始,构建会用一条点名被丢弃键的错误拒绝这种情况,例如 chrome:manifest_version applies only to Chrome builds, so the edge build has no manifest_version.。到 4.1.20 为止,构建会照常写出 manifest,浏览器随后才拒绝它。
请写一个不带前缀的 manifest_version 作为基础值,只给例外情况加前缀:
chromium:manifest_version。
最佳实践
- 共享默认值保持无前缀:把通用字段写在普通 manifest 键中,只对浏览器差异的部分加前缀。
- 行为分歧时再加前缀:当运行时要求不同时再使用浏览器前缀。
- 在持续集成 (CI) 中按目标分别构建:分别生成并验证每个浏览器的产物(
dist/<browser>),以便尽早发现兼容性回归。 - 用 MDN 验证:在添加仅特定浏览器可用的设置前,使用 MDN Web Docs 确认支持情况。

