Skip to content

入口点

WXT 使用 entrypoints/ 目录中的文件作为打包扩展的输入。它们可以是 HTML、JS、CSS,或 Vite 支持的任何变体文件类型(TS、JSX、SCSS 等)。

目录结构

entrypoints/ 目录中,入口点定义为单个文件或包含 index 文件的目录。

html
📂 entrypoints/
   📄 {name}.{ext}
html
📂 entrypoints/
   📂 {name}/
      📄 index.{ext}

入口点的 name 决定了入口点的类型。例如,要添加一个 "Background" 入口点,以下任一文件均可:

html
📂 entrypoints/
   📄 background.ts
html
📂 entrypoints/
   📂 background/
      📄 index.ts

请参阅入口点类型部分,了解完整的入口点列表及其文件名模式。

包含其他文件

使用入口点目录 entrypoints/{name}/index.{ext} 时,你可以在 index 文件旁边添加相关文件。

html
📂 entrypoints/
   📂 popup/
      📄 index.html     ← 此文件是入口点
      📄 main.ts
      📄 style.css
   📂 background/
      📄 index.ts       ← 此文件是入口点
      📄 alarms.ts
      📄 messaging.ts
   📂 youtube.content/
      📄 index.ts       ← 此文件是入口点
      📄 style.css

DANGER

不要将与入口点相关的文件直接放在 entrypoints/ 目录下。WXT 会将它们视为入口点并尝试构建,这通常会导致错误。

应当为该入口点使用目录:

html
📂 entrypoints/
   📄 popup.html 
   📄 popup.ts 
   📄 popup.css 
   📂 popup/ 
      📄 index.html 
      📄 main.ts 
      📄 style.css 

深层嵌套入口点

虽然 entrypoints/ 目录可能类似于其他 Web 框架(如 Nuxt 或 Next.js)的 pages/ 目录,但它不支持以相同方式深层嵌套入口点

入口点必须在零层或一层深度内,WXT 才能发现并构建它们:

html
📂 entrypoints/
   📂 youtube/ 
       📂 content/ 
          📄 index.ts 
          📄 ... 
       📂 injected/ 
          📄 index.ts 
          📄 ... 
   📂 youtube.content/ 
      📄 index.ts 
      📄 ... 
   📂 youtube-injected/ 
      📄 index.ts 
      📄 ... 

未列出的入口点

在 Web 扩展中,有两种类型的入口点:

  1. 已列出的(Listed):在 manifest.json 中引用
  2. 未列出的(Unlisted):未在 manifest.json 中引用

在 WXT 文档的其余部分中,已列出的入口点按名称引用。例如:

  • Popup
  • Options
  • Background
  • Content Script

然而,并非所有 Web 扩展中的入口点都列在 Manifest 中。有些未列在 Manifest 中,但仍被扩展使用。例如:

  • 扩展安装后在新标签页中显示的欢迎页面
  • Content Script 注入到主世界(main world)的 JS 文件

有关如何添加未列出入口点的更多详情,请参阅:

定义 Manifest 选项

大多数已列出的入口点都有需要添加到 manifest.json 的选项。然而在 WXT 中,你无需在单独的文件中定义这些选项,而是_在入口点文件内部定义这些选项_。

例如,以下是为 Content Script 定义 matches 的方式:

ts
export default defineContentScript({
  matches: ['*://*.wxt.dev/*'],
  main() {
    // ...
  },
});

对于 HTML 入口点,选项通过 <meta> 标签配置。例如,要为 MV2 的 Popup 使用 page_action

html
<!doctype html>
<html lang="en">
  <head>
    <meta name="manifest.type" content="page_action" />
  </head>
</html>

请参阅入口点类型部分,了解每个入口点内部可配置的选项列表及其定义方式。

构建扩展时,WXT 会查看你在入口点中定义的选项,并相应地生成 Manifest。

入口点类型

Background

Chrome 文档Firefox 文档

FilenameOutput Path
entrypoints/background.[jt]s/background.js
entrypoints/background/index.[jt]s/background.js
ts
export default defineBackground(() => {
  // Background 加载时执行
});
ts
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 函数之外。

ts
browser.action.onClicked.addListener(() => { 
  // ...
}); 

export default defineBackground(() => {
  browser.action.onClicked.addListener(() => { 
    // ...
  }); 
});

请参阅入口点加载器文档了解更多详情。

Bookmarks

Chrome 文档Firefox 文档

FilenameOutput Path
entrypoints/bookmarks.html/bookmarks.html
entrypoints/bookmarks/index.html/bookmarks.html
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

Chrome 文档Firefox 文档

FilenameOutput 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
ts
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 函数之外。

ts
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

Chrome 文档Firefox 文档

FilenameOutput Path
entrypoints/devtools.html/devtools.html
entrypoints/devtools/index.html/devtools.html
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

Chrome 文档Firefox 文档

FilenameOutput Path
entrypoints/history.html/history.html
entrypoints/history/index.html/history.html
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

Chrome 文档Firefox 文档

FilenameOutput Path
entrypoints/newtab.html/newtab.html
entrypoints/newtab/index.html/newtab.html
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

Chrome 文档Firefox 文档

FilenameOutput Path
entrypoints/options.html/options.html
entrypoints/options/index.html/options.html
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>

Chrome 文档Firefox 文档

FilenameOutput Path
entrypoints/popup.html/popup.html
entrypoints/popup/index.html/popup.html
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

Chrome 文档

仅 Chromium

Firefox 不支持沙盒页面。

FilenameOutput 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
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

Chrome 文档Firefox 文档

FilenameOutput 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`
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

FilenameOutput 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
css
body {
  /* ... */
}

按照 Vite 的指南设置你选择的预处理器:https://vitejs.dev/guide/features.html#css-pre-processors

CSS 入口点始终是未列出的。要将 CSS 添加到 Content Script,请参阅 Content Script 文档。

未列出的页面

FilenameOutput Path
entrypoints/{name}.html/{name}.html
entrypoints/{name}/index.html/{name}.html
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 访问:

ts
const url = browser.runtime.getURL('/{name}.html');

console.log(url); // "chrome-extension://{id}/{name}.html"
window.open(url); // 在新标签页中打开页面

未列出的脚本

FilenameOutput Path
entrypoints/{name}.[jt]sx?/{name}.js
entrypoints/{name}/index.[jt]sx?/{name}.js
ts
export default defineUnlistedScript(() => {
  // 脚本加载时执行
});
ts
export default defineUnlistedScript({
  // 设置 include/exclude,可从某些构建中移除此脚本
  include: undefined | string[],
  exclude: undefined | string[],

  main() {
    // 脚本加载时执行
  },
});

在运行时,未列出的脚本可从 /{name}.js 访问:

ts
const url = browser.runtime.getURL('/{name}.js');

console.log(url); // "chrome-extension://{id}/{name}.js"

你需要负责在需要的地方加载/运行这些脚本。如有必要,别忘了将脚本和/或任何相关资源添加到 web_accessible_resources

定义未列出的脚本时,请注意 WXT 会在构建过程中在 NodeJS 环境中导入此文件。这意味着你不能将任何运行时代码放在 main 函数之外。

ts
document.querySelectorAll('a').forEach((anchor) => { 
  // ...
}); 

export default defineUnlistedScript(() => {
  document.querySelectorAll('a').forEach((anchor) => { 
    // ...
  }); 
});

请参阅入口点加载器文档了解更多详情。