wxt.config.ts 与 manifest 详解
本教程共 45 篇 · 第 25 篇 · 更新于 2026-08-13 · 约 4 分钟阅读
本节目标:看懂 WXT 的总配置入口 wxt.config.ts,弄清清单(manifest.json)是怎么拼出来的,学会逐字段配置清单,并按浏览器分支返回不同内容。
前几章里,你已经在每个项目里见过 wxt.config.ts。它位于项目根目录,导出 defineConfig(...) 的返回值,是 WXT 的总配置入口。manifest、vite、hooks、modules、alias、imports 这些配置都写在它里面。这一章先把最核心的 manifest 字段讲透,其余字段在后续章节展开。
清单不是手写的,是拼出来的
原生开发扩展时,你要亲手维护 manifest.json。WXT 的源码目录里没有这个文件,清单(manifest)由多个来源合并生成:
wxt.config.ts里的全局manifest配置- 入口点文件内定义的选项,如 §07 所述的 manifest 选项
- WXT 模块(§31)对清单的修改
- 构建钩子(§30)对清单的修改
执行 wxt build 后,合并结果输出到 .output/{目标}/manifest.json。也就是说,你写的是「生成清单的规则」,而不是清单本身。
全局 manifest 配置
在 wxt.config.ts 里加一个 manifest 键,就能往清单里塞任意属性:
export default defineConfig({
manifest: {
// 手动补充的字段都放这里
},
});
manifest 也可以是函数,用 JavaScript 按目标浏览器、模式等条件动态生成:
export default defineConfig({
manifest: ({ browser, manifestVersion, mode, command }) => {
return {
// ...
};
},
});
函数的四个参数分别表示:目标浏览器、目标清单版本、构建模式、执行的命令。这是实现「一套配置,多端输出」的关键,后面会用到。
常用字段逐个看
name:不配置时,默认取 package.json 的 name 字段。
version 与 version_name:都来自 package.json 的 version。version_name 原样保留;version 会被清理掉非法后缀。比如版本号写 "1.3.0-alpha2",产物清单里就是 version: "1.3.0"、version_name: "1.3.0-alpha2"。package.json 没写版本时,默认 "0.0.0"。
icons:WXT 会自动扫描 public/ 目录,按 icon-16.png、icon-48.png 这类命名发现图标(支持 16/24/32/48/96/128 等尺寸)。不想改文件名时,也可以手动指定:
export default defineConfig({
manifest: {
icons: {
16: '/extension-icon-16.png',
48: '/extension-icon-48.png',
128: '/extension-icon-128.png',
},
},
});
想要「一张原图自动生成所有尺寸」,用 @wxt-dev/auto-icons 模块,见 §32。
theme_icons(Firefox 专属):Firefox 支持按浏览器主题在亮色/暗色图标间切换。目标为 Firefox 时,WXT 会自动发现 public/ 里配对的 icon-light-{size}.png 和 icon-dark-{size}.png,只有亮暗两个文件都齐全的尺寸才会进入 theme_icons。当前只识别 .png。如果你在配置里手动写了 action.theme_icons,WXT 不会覆盖它。
permissions:绝大多数权限要手动声明。只有两种自动添加的情况:开发模式下自动加 tabs 和 scripting(热重载需要);存在 sidepanel 入口时自动加 sidepanel 权限。权限的详细讲法见 §21,这里只提醒一句:不同浏览器支持的权限不同,最好用函数分支:
export default defineConfig({
manifest: ({ browser }) => ({
permissions:
browser === 'chrome'
? ['storage', 'favicon', 'declarativeNetRequest']
: ['storage', 'webRequest'],
}),
});
host_permissions:写法与 permissions 类似。如果同时面向 MV2 和 MV3,注意两个版本的 host 权限规则不同,建议按 manifestVersion 分支:
export default defineConfig({
manifest: ({ manifestVersion }) => ({
host_permissions: manifestVersion === 2 ? ['https://a.com/*'] : ['https://b.com/*'],
}),
});
default_locale:做国际化时使用,配合 __MSG_xxx__ 占位符,详见 §19:
export default defineConfig({
manifest: {
name: '__MSG_extName__',
description: '__MSG_extDescription__',
default_locale: 'en',
},
});
action:MV3 的 action 在 MV2 下会自动回退成 browser_action。有 popup 入口时,WXT 会自动生成带 popup 的 action(§08)。如果你只想用 activeTab 权限或 browser.action.onClicked 事件、不想要弹窗,做法是:删掉 popup 入口,再在清单里加一个空 action: {}。想用 MV2 的 page_action,则在 HTML 的 <head> 里加 <meta name="manifest.type" content="page_action" />,或同时声明 action 与 page_action 两个键。
MV2 与 MV3:写一份,跑两版
WXT 的兼容策略很省心:能写 MV3 格式就写 MV3 格式,目标换成 MV2 时,WXT 自动转换。比如下面这份配置:
export default defineConfig({
manifest: {
action: {
default_title: 'Some Title',
},
web_accessible_resources: [
{
matches: ['*://*.google.com/*'],
resources: ['icon/*.png'],
},
],
},
});
构建出来的两个版本分别是:
{
"manifest_version": 2,
"browser_action": {
"default_title": "Some Title"
},
"web_accessible_resources": ["icon/*.png"]
}
{
"manifest_version": 3,
"action": {
"default_title": "Some Title"
},
"web_accessible_resources": [
{
"matches": ["*://*.google.com/*"],
"resources": ["icon/*.png"]
}
]
}
只属于某个清单版本的属性,面向另一版本构建时会被自动剥掉,不用你操心。
按浏览器分支返回清单
真实项目里,不同浏览器的要求差异很大。看一个生产级项目(mkext)的做法:
export default defineConfig({
manifest: ({ browser }) => {
const isFirefox = browser === 'firefox';
const manifest = {
name: '__MSG_extensionName__',
default_locale: 'en',
key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...', // Chromium 专用
permissions: ['identity', 'storage', 'tabs'],
oauth2: {
client_id: import.meta.env.VITE_GOOGLE_EXTENSION_CLIENT_ID,
scopes: ['openid', 'email', 'profile'],
}, // Chromium 专用
minimum_chrome_version: isFirefox ? undefined : '114',
browser_specific_settings: isFirefox
? { gecko: { id: 'mkext-template@mkfast.dev' } }
: undefined,
};
if (isFirefox) {
const { key, oauth2, ...firefoxManifest } = manifest;
return firefoxManifest; // 剥离 Chromium 专有字段
}
return manifest;
},
});
这里有两个值得记住的点:
minimum_chrome_version:给 Chromium 系浏览器声明最低版本。mkext 写"114",因为它的 sidepanel 入口需要 Chrome 114,声明下限可以避免老版本浏览器装上一个半残的扩展。browser_specific_settings.gecko:Firefox 用它声明扩展 ID 等信息。key和oauth2是 Chromium 专有:Firefox 靠gecko.id识别扩展,没有identity.getAuthToken(),AMO 审核还会把未知清单字段当问题标记。所以 mkext 在 Firefox 分支里把这两个字段解构剥离,顺带避免泄露 OAuth client ID。
Note
matches相关规则在 §11 内容脚本一章有详细说明;权限体系整体讲解在 §21。本章只负责「配置怎么写」。
小结
- manifest 由 WXT 从多个来源生成:入口文件选项、
public/图标、config 里的 manifest 函数。 - 常用字段逐个看:name / version / icons / permissions / action,跨浏览器差异用 manifest 函数分支。
- 图标、权限等能自动发现和补全的,别手写。