入口点
WXT 使用 entrypoints/ 目录中的文件作为打包扩展的输入。它们可以是 HTML、JS、CSS,或 Vite 支持的任何变体文件类型(TS、JSX、SCSS 等)。
目录结构
在 entrypoints/ 目录中,入口点定义为单个文件或包含 index 文件的目录。
📂 entrypoints/
📄 {name}.{ext}📂 entrypoints/
📂 {name}/
📄 index.{ext}入口点的 name 决定了入口点的类型。例如,要添加一个 "Background" 入口点,以下任一文件均可:
📂 entrypoints/
📄 background.ts📂 entrypoints/
📂 background/
📄 index.ts请参阅入口点类型部分,了解完整的入口点列表及其文件名模式。
包含其他文件
使用入口点目录 entrypoints/{name}/index.{ext} 时,你可以在 index 文件旁边添加相关文件。
📂 entrypoints/
📂 popup/
📄 index.html ← 此文件是入口点
📄 main.ts
📄 style.css
📂 background/
📄 index.ts ← 此文件是入口点
📄 alarms.ts
📄 messaging.ts
📂 youtube.content/
📄 index.ts ← 此文件是入口点
📄 style.cssDANGER
不要将与入口点相关的文件直接放在 entrypoints/ 目录下。WXT 会将它们视为入口点并尝试构建,这通常会导致错误。
应当为该入口点使用目录:
📂 entrypoints/
📄 popup.html
📄 popup.ts
📄 popup.css
📂 popup/
📄 index.html
📄 main.ts
📄 style.css 深层嵌套入口点
虽然 entrypoints/ 目录可能类似于其他 Web 框架(如 Nuxt 或 Next.js)的 pages/ 目录,但它不支持以相同方式深层嵌套入口点。
入口点必须在零层或一层深度内,WXT 才能发现并构建它们:
📂 entrypoints/
📂 youtube/
📂 content/
📄 index.ts
📄 ...
📂 injected/
📄 index.ts
📄 ...
📂 youtube.content/
📄 index.ts
📄 ...
📂 youtube-injected/
📄 index.ts
📄 ... 未列出的入口点
在 Web 扩展中,有两种类型的入口点:
- 已列出的(Listed):在
manifest.json中引用 - 未列出的(Unlisted):未在
manifest.json中引用
在 WXT 文档的其余部分中,已列出的入口点按名称引用。例如:
- Popup
- Options
- Background
- Content Script
然而,并非所有 Web 扩展中的入口点都列在 Manifest 中。有些未列在 Manifest 中,但仍被扩展使用。例如:
- 扩展安装后在新标签页中显示的欢迎页面
- Content Script 注入到主世界(main world)的 JS 文件
有关如何添加未列出入口点的更多详情,请参阅:
定义 Manifest 选项
大多数已列出的入口点都有需要添加到 manifest.json 的选项。然而在 WXT 中,你无需在单独的文件中定义这些选项,而是_在入口点文件内部定义这些选项_。
例如,以下是为 Content Script 定义 matches 的方式:
export default defineContentScript({
matches: ['*://*.wxt.dev/*'],
main() {
// ...
},
});对于 HTML 入口点,选项通过 <meta> 标签配置。例如,要为 MV2 的 Popup 使用 page_action:
<!doctype html>
<html lang="en">
<head>
<meta name="manifest.type" content="page_action" />
</head>
</html>请参阅入口点类型部分,了解每个入口点内部可配置的选项列表及其定义方式。
构建扩展时,WXT 会查看你在入口点中定义的选项,并相应地生成 Manifest。
入口点类型
Background
| Filename | Output Path | |
|---|---|---|
entrypoints/background.[jt]s | /background.js | |
entrypoints/background/index.[jt]s | /background.js |
export default defineBackground(() => {
// Background 加载时执行
});export default defineBackground({
// 设置 Manifest 选项
persistent: undefined | true | false,
type: undefined | 'module',
// 设置 include/exclude,可从某些构建中移除 background
include: undefined | string[],
exclude: undefined | string[],
main() {
// Background 加载时执行,不能是异步函数
},
});对于 MV2,background 作为脚本添加到后台页面。对于 MV3,background 变为 Service Worker。
定义 background 入口点时,请注意 WXT 会在构建过程中在 NodeJS 环境中导入此文件。这意味着你不能将任何运行时代码放在 main 函数之外。
browser.action.onClicked.addListener(() => {
// ...
});
export default defineBackground(() => {
browser.action.onClicked.addListener(() => {
// ...
});
});请参阅入口点加载器文档了解更多详情。
Bookmarks
| Filename | Output Path | |
|---|---|---|
entrypoints/bookmarks.html | /bookmarks.html | |
entrypoints/bookmarks/index.html | /bookmarks.html |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>当你定义 Bookmarks 入口点时,WXT 会自动更新 Manifest,用你的 HTML 页面覆盖浏览器的书签页面。
Content Scripts
| Filename | Output Path | |
|---|---|---|
entrypoints/content.[jt]sx? | /content-scripts/content.js | |
entrypoints/content/index.[jt]sx? | /content-scripts/content.js | |
entrypoints/{name}.content.[jt]sx? | /content-scripts/{name}.js | |
entrypoints/{name}.content/index.[jt]sx? | /content-scripts/{name}.js |
export default defineContentScript({
// 设置 Manifest 选项
matches: string[],
excludeMatches: undefined | [],
includeGlobs: undefined | [],
excludeGlobs: undefined | [],
allFrames: undefined | true | false,
runAt: undefined | 'document_start' | 'document_end' | 'document_idle',
matchAboutBlank: undefined | true | false,
matchOriginAsFallback: undefined | true | false,
world: undefined | 'ISOLATED' | 'MAIN',
// 设置 include/exclude,可从某些构建中移除 Content Script
include: undefined | string[],
exclude: undefined | string[],
// 配置 CSS 注入页面的方式
cssInjectionMode: undefined | "manifest" | "manual" | "ui",
// 配置 Content Script 的注册方式/时机
registration: undefined | "manifest" | "runtime",
main(ctx: ContentScriptContext) {
// Content Script 加载时执行,可以是异步函数
},
});定义 Content Script 入口点时,请注意 WXT 会在构建过程中在 NodeJS 环境中导入此文件。这意味着你不能将任何运行时代码放在 main 函数之外。
const container = document.createElement('div');
document.body.append(container);
export default defineContentScript({
main: function () {
const container = document.createElement('div');
document.body.append(container);
},
});请参阅入口点加载器文档了解更多详情。
关于在 Content Script 中创建 UI 和包含 CSS 的更多信息,请参阅 Content Script UI。
Devtools
| Filename | Output Path | |
|---|---|---|
entrypoints/devtools.html | /devtools.html | |
entrypoints/devtools/index.html | /devtools.html |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>请参考 Devtools 示例 来添加不同的面板和窗格。
History
| Filename | Output Path | |
|---|---|---|
entrypoints/history.html | /history.html | |
entrypoints/history/index.html | /history.html |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>当你定义 History 入口点时,WXT 会自动更新 Manifest,用你的 HTML 页面覆盖浏览器的历史记录页面。
Newtab
| Filename | Output Path | |
|---|---|---|
entrypoints/newtab.html | /newtab.html | |
entrypoints/newtab/index.html | /newtab.html |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>当你定义 Newtab 入口点时,WXT 会自动更新 Manifest,用你的 HTML 页面覆盖浏览器的新标签页。
Options
| Filename | Output Path | |
|---|---|---|
entrypoints/options.html | /options.html | |
entrypoints/options/index.html | /options.html |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Options Title</title>
<!-- 自定义 Manifest 选项 -->
<meta name="manifest.open_in_tab" content="true|false" />
<meta name="manifest.chrome_style" content="true|false" />
<meta name="manifest.browser_style" content="true|false" />
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>Popup
| Filename | Output Path | |
|---|---|---|
entrypoints/popup.html | /popup.html | |
entrypoints/popup/index.html | /popup.html |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- 在 Manifest 中设置 `action.default_title` -->
<title>Default Popup Title</title>
<!-- 自定义 Manifest 选项 -->
<meta
name="manifest.default_icon"
content="{
16: '/icon-16.png',
24: '/icon-24.png',
...
}"
/>
<meta name="manifest.type" content="page_action|browser_action" />
<meta name="manifest.browser_style" content="true|false" />
<!-- 仅 Firefox:操作按钮的放置位置 -->
<meta
name="manifest.default_area"
content="navbar|menupanel|tabstrip|personaltoolbar"
/>
<!-- 仅 Firefox:浅色/深色主题图标 -->
<meta
name="manifest.theme_icons"
content="[
{ light: '/icon-light-16.png', dark: '/icon-dark-16.png', size: 16 },
{ light: '/icon-light-32.png', dark: '/icon-dark-32.png', size: 32 }
]"
/>
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>Sandbox
仅 Chromium
Firefox 不支持沙盒页面。
| Filename | Output Path | |
|---|---|---|
entrypoints/sandbox.html | /sandbox.html | |
entrypoints/sandbox/index.html | /sandbox.html | |
entrypoints/{name}.sandbox.html | /{name}.html | |
entrypoints/{name}.sandbox/index.html | /{name}.html |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>Side Panel
| Filename | Output Path | |
|---|---|---|
entrypoints/sidepanel.html | /sidepanel.html | |
entrypoints/sidepanel/index.html | /sidepanel.html | |
entrypoints/{name}.sidepanel.html | /{name}.html` | |
entrypoints/{name}.sidepanel/index.html | /{name}.html` |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Default Side Panel Title</title>
<!-- 自定义 Manifest 选项 -->
<meta
name="manifest.default_icon"
content="{
16: '/icon-16.png',
24: '/icon-24.png',
...
}"
/>
<meta name="manifest.open_at_install" content="true|false" />
<meta name="manifest.browser_style" content="true|false" />
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>在 Chrome 中,Side Panel 使用 side_panel API,而 Firefox 使用 sidebar_action API。
未列出的 CSS
| Filename | Output Path | |
|---|---|---|
entrypoints/{name}.(css|scss|sass|less|styl|stylus) | /{name}.css | |
entrypoints/{name}/index.(css|scss|sass|less|styl|stylus) | /{name}.css | |
entrypoints/content.(css|scss|sass|less|styl|stylus) | /content-scripts/content.css | |
entrypoints/content/index.(css|scss|sass|less|styl|stylus) | /content-scripts/content.css | |
entrypoints/{name}.content.(css|scss|sass|less|styl|stylus) | /content-scripts/{name}.css | |
entrypoints/{name}.content/index.(css|scss|sass|less|styl|stylus) | /content-scripts/{name}.css |
body {
/* ... */
}按照 Vite 的指南设置你选择的预处理器:https://vitejs.dev/guide/features.html#css-pre-processors
CSS 入口点始终是未列出的。要将 CSS 添加到 Content Script,请参阅 Content Script 文档。
未列出的页面
| Filename | Output Path | |
|---|---|---|
entrypoints/{name}.html | /{name}.html | |
entrypoints/{name}/index.html | /{name}.html |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- 设置 include/exclude,可从某些构建中移除此页面 -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>在运行时,未列出的页面可通过 /{name}.html 访问:
const url = browser.runtime.getURL('/{name}.html');
console.log(url); // "chrome-extension://{id}/{name}.html"
window.open(url); // 在新标签页中打开页面未列出的脚本
| Filename | Output Path | |
|---|---|---|
entrypoints/{name}.[jt]sx? | /{name}.js | |
entrypoints/{name}/index.[jt]sx? | /{name}.js |
export default defineUnlistedScript(() => {
// 脚本加载时执行
});export default defineUnlistedScript({
// 设置 include/exclude,可从某些构建中移除此脚本
include: undefined | string[],
exclude: undefined | string[],
main() {
// 脚本加载时执行
},
});在运行时,未列出的脚本可从 /{name}.js 访问:
const url = browser.runtime.getURL('/{name}.js');
console.log(url); // "chrome-extension://{id}/{name}.js"你需要负责在需要的地方加载/运行这些脚本。如有必要,别忘了将脚本和/或任何相关资源添加到 web_accessible_resources。
定义未列出的脚本时,请注意 WXT 会在构建过程中在 NodeJS 环境中导入此文件。这意味着你不能将任何运行时代码放在 main 函数之外。
document.querySelectorAll('a').forEach((anchor) => {
// ...
});
export default defineUnlistedScript(() => {
document.querySelectorAll('a').forEach((anchor) => {
// ...
});
});请参阅入口点加载器文档了解更多详情。