前端开发工具构建工具插件系统【免费下载链接】wxt⚡ Next-gen Web Extension Framework项目地址https://gitcode.com/gh_mirrors/wx/wxt点击查看免费下载Entrypoints入口点是 WXT 扩展开发的核心概念——entrypoints/目录下的每一个文件或目录都是 WXT 打包扩展的输入也是 manifest.json 自动生成的依据。本篇指南基于 WXT 官方文档docs/guide/essentials/entrypoints.md与源码实现find-entrypoints.ts完整梳理入口点的目录规则、13 类入口点的文件名模式与可配置选项并结合 wxt-demo 的真实示例帮助你彻底掌握声明式定义扩展的 WXT 开发方式。什么是 Entrypoints在 WXT 中entrypoints/目录是扩展的装配车间目录内的文件作为打包时的输入inputs参与构建产物生成。它们可以是 HTML、JS、CSS也可以是 Vite 支持的任意变体——TypeScript.ts、JSX.tsx/.jsx、SCSS、Sass、Less、Stylus 等均无需额外配置即可直接使用。入口点不仅决定构建什么还决定manifest 里写什么。WXT 会读取每个入口点内声明的选项manifest options在构建时自动生成对应的 manifest.json省去了手动维护background、content_scripts、action等清单字段的繁琐工作。目录结构约定在entrypoints/目录内一个入口点既可以是单个文件也可以是一个包含index文件的目录两种写法等价 entrypoints/ {name}.{ext} entrypoints/ {name}/ index.{ext}入口点的name直接决定它的类型。例如要添加一个 Background 入口点以下两种文件布局任选其一 entrypoints/ background.ts entrypoints/ background/ index.ts从源码看WXT 通过PATH_GLOB_TO_TYPE_MAP这张文件名 glob 模式映射表来识别入口点类型见 find-entrypoints.ts。getEntrypointName则取相对路径中第一个.或/之前的片段作为入口点名称见 entrypoints.ts。因此youtube.content.ts的名称是youtube、类型是 content-script而example-tsx.content.tsx见 wxt-demo也能被正确识别。在入口点目录中放置相关文件使用目录形式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.css禁止把相关文件直接放进 entrypoints/ 根目录千万不要把某个入口点的附属文件直接放在entrypoints/目录下——WXT 会把它们当成独立入口点尝试构建通常会直接报错。正确做法是把这些文件放进对应入口点的目录中 entrypoints/ popup.html ✗ 错误应使用目录 popup.ts ✗ 错误 popup.css ✗ 错误 popup/ ✓ 正确 index.html main.ts style.css不支持深层嵌套entrypoints/目录虽然在观感上类似 Nuxt 或 Next.js 的pages/目录但WXT 不支持同样的深层嵌套。入口点只能位于entrypoints/下零层或一层即entrypoints/{name}.{ext}或entrypoints/{name}/index.{ext}嵌套更深将无法被发现和构建 entrypoints/ youtube/ ✗ 错误嵌套过深 content/ index.ts ... injected/ index.ts ... youtube.content/ ✓ 正确用命名后缀区分 index.ts ... youtube-injected/ ✓ 正确用命名后缀区分 index.ts ...正如PATH_GLOB_TO_TYPE_MAP所示WXT 只匹配entrypoints/下一层的 glob 模式如*.content.[jt]s?(x)、*/index.html更深层的路径不在匹配范围内这就是嵌套不被支持的根源。Listed 与 Unlisted两类入口点Web 扩展中存在两种入口点Listed已列出被引用在manifest.json中的入口点例如 Popup、Options、Background、Content Script 等。WXT 文档中通常直接用名字称呼它们。Unlisted未列出不出现在 manifest 中但扩展运行时会用到的入口点例如扩展安装后在新标签页展示的欢迎页由内容脚本注入到主世界main world的 JS 文件。Unlisted 入口点的具体用法见下文 Unlisted Pages、Unlisted Scripts 与 Unlisted CSS。在入口点内部定义 manifest 选项大多数 listed 入口点需要在manifest.json中声明对应选项。与另起一个文件维护 manifest的传统方式不同WXT 把选项直接定义在入口点文件内部对 JS 类入口点选项作为defineXxx工厂函数的参数传入。例如给内容脚本声明matchesexport default defineContentScript({ matches: [*://*.wxt.dev/*], main() { // ... }, });对 HTML 类入口点选项通过meta标签配置。例如为 MV2 popup 使用page_action!doctype html html langen head meta namemanifest.type contentpage_action / /head /html构建时WXT 会收集入口点中声明的选项据此生成 manifest.json。meta 标签的解析机制源码级从源码看HTML 入口点的选项解析逻辑位于 find-entrypoints.ts 的importHtmlEntrypoint函数只处理name以manifest.或wxt.前缀开头的meta标签其余标签直接忽略manifest.前缀后的内容会通过camelCase转为驼峰键名例如manifest.default_icon→defaultIconmeta的content属性会优先尝试用 JSON5 解析成结构化数据这就是 Popup 的default_icon能写成对象字面量、theme_icons能写成数组的原因解析失败才退化为原始字符串title标签的内容也会被读取作为title选项例如 Popup 的default_title。按浏览器差异化配置选项WXT 的入口点选项还支持per-browser按浏览器差异化任何选项都可以写成{ chrome: ..., firefox: ... }这种按目标浏览器取值的形式。源码中的resolvePerBrowserOption/resolvePerBrowserOptions见 entrypoints.ts会在解析时根据当前构建目标浏览器替换成对应值其中defaultIcon是唯一的特例——它是 Record 结构被显式排除在解析之外以避免与 per-browser 语法冲突。这意味着你可以在同一个入口点里为 Chrome 与 Firefox 声明不同的runAt、persistent等行为。Entrypoint 类型详解下面按类型逐一说明文件名模式、可配置选项与注意事项。文件名模式中的[jt]sx?表示.js、.ts、.jsx、.tsx均可用[jt]s表示.js或.ts。Background文件名模式background.[jt]s→ 输出为background.jsbackground/index.[jt]s→ 输出为background.js最小写法export default defineBackground(() { // 后台脚本加载时执行 });带 manifest 选项的写法export default defineBackground({ // 设置 manifest 选项 persistent: undefined | true | false, type: undefined | module, // 设置在部分浏览器构建中是否移除该入口点 include: undefined | string[], exclude: undefined | string[], main() { // 后台脚本加载时执行注意不能是 async }, });关键行为在 MV2 中background 作为脚本挂到后台页面background page在 MV3 中background 成为 Service Worker。构建过程中WXT 会在 Node.js 环境中导入该文件以读取配置因此任何运行时逻辑都不能写在main函数之外。下面这种写法是错误的browser.action.onClicked.addListener(() { // ✗ 错误模块顶层代码会在 Node 构建环境执行 // ... }); export default defineBackground(() { browser.action.onClicked.addListener(() { // ✓ 正确放进 main 内 // ... }); });源码层面defineBackground是一个纯类型包装函数见 define-background.ts传入函数时包装成{ main: fn }传入对象时原样返回。类型定义BackgroundDefinition明确要求main(): void见 types.ts即 background 的 main 不允许是异步的。WXT 的加载机制详见 Entrypoint Loaders。Bookmarks文件名模式bookmarks.html→ 输出为bookmarks.htmlbookmarks/index.html→ 输出为bookmarks.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleTitle/title !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /html定义 Bookmarks 入口点后WXT 会自动修改 manifest用你的 HTML 页面覆盖浏览器的书签管理页。Content Scripts文件名模式content.[jt]sx?→ 输出为content-scripts/content.jscontent/index.[jt]sx?→ 输出为content-scripts/content.js{name}.content.[jt]sx?→ 输出为content-scripts/{name}.js{name}.content/index.[jt]sx?→ 输出为content-scripts/{name}.js注意命名内容脚本{name}.content.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: undefined | string[], exclude: undefined | string[], // 配置 CSS 注入页面的方式 cssInjectionMode: undefined | manifest | manual | ui, // 配置内容脚本的注册方式 registration: undefined | manifest | runtime, main(ctx: ContentScriptContext) { // 内容脚本加载时执行可以是 async }, });关键行为与 background 相同构建时该文件也会在 Node.js 环境被导入所以运行时代码必须放进mainconst container document.createElement(div); // ✗ 错误 document.body.append(container); // ✗ 错误 export default defineContentScript({ main: function () { const container document.createElement(div); // ✓ 正确 document.body.append(container); // ✓ 正确 }, });defineContentScript同样是类型包装函数见 define-content-script.ts运行时不做任何处理。在 wxt-demo 中可以看到content.ts、iframe.content.ts、location-change.content.ts、main-world.content.ts、example-tsx.content.tsx、automount.content/、ui.content/、injected.content/等多个内容脚本实例见 entrypoints 目录。内容脚本 UI 的创建方式与 CSS 注入配置详见 Content Scripts 指南。Devtools文件名模式devtools.html→ 输出为devtools.htmldevtools/index.html→ 输出为devtools.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /htmlDevtools 入口点用于注册浏览器的开发者工具面板如需添加不同的面板panels与窗格panes可以参考 WXT 官方的 devtools-extension 示例项目。History文件名模式history.html→ 输出为history.htmlhistory/index.html→ 输出为history.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleTitle/title !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /html定义 History 入口点后WXT 会自动修改 manifest用你的 HTML 页面覆盖浏览器的历史记录页。Newtab文件名模式newtab.html→ 输出为newtab.htmlnewtab/index.html→ 输出为newtab.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleTitle/title !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /html定义 Newtab 入口点后WXT 会自动修改 manifest用你的 HTML 页面覆盖浏览器的新标签页。Options文件名模式options.html→ 输出为options.htmloptions/index.html→ 输出为options.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleOptions Title/title !-- 自定义 manifest 选项 -- meta namemanifest.open_in_tab contenttrue|false / meta namemanifest.chrome_style contenttrue|false / meta namemanifest.browser_style contenttrue|false / !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /htmlopen_in_tab决定选项页是在新标签页打开还是嵌入在扩展管理界面中chrome_style/browser_style则分别对应 Chrome 与 Firefox 的浏览器内置样式。wxt-demo 中的 options 入口点 就是该模式的完整示例配套main.ts与style.css。Popup文件名模式popup.html→ 输出为popup.htmlpopup/index.html→ 输出为popup.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / !-- 设置 manifest 中的 action.default_title -- titleDefault Popup Title/title !-- 自定义 manifest 选项 -- meta namemanifest.default_icon content{ 16: /icon-16.png, 24: /icon-24.png, ... } / meta namemanifest.type contentpage_action|browser_action / meta namemanifest.browser_style contenttrue|false / !-- 仅 Firefox设置动作按钮的放置位置 -- meta namemanifest.default_area contentnavbar|menupanel|tabstrip|personaltoolbar / !-- 仅 Firefox亮色/暗色主题下的图标 -- meta namemanifest.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 } ] / !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /htmlPopup 是扩展工具栏弹出窗上述选项最终都会映射到 manifest 的actionMV3或browser_action/page_actionMV2字段。源码中getPopupEntrypoint见 find-entrypoints.ts会做额外的规整title被重命名为defaultTitletype被映射为actionType非page_action一律视为browser_action并通过 getter/setter 保持与旧字段mv2Key的同步兼容。wxt-demo 的 popup.html 是真实示例。Sandbox警告仅 Chromium 支持沙箱页面sandboxed pagesFirefox 不支持。文件名模式sandbox.html→ 输出为sandbox.htmlsandbox/index.html→ 输出为sandbox.html{name}.sandbox.html→ 输出为{name}.html{name}.sandbox/index.html→ 输出为{name}.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleTitle/title !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /htmlwxt-demo 中提供了 sandbox.html 与目录形式的 example.sandbox 两个示例。命名沙箱{name}.sandbox常用于为扩展提供不受扩展 CSP 限制的 iframe 运行环境。Side Panel文件名模式sidepanel.html→ 输出为sidepanel.htmlsidepanel/index.html→ 输出为sidepanel.html{name}.sidepanel.html→ 输出为{name}.html{name}.sidepanel/index.html→ 输出为{name}.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleDefault Side Panel Title/title !-- 自定义 manifest 选项 -- meta namemanifest.default_icon content{ 16: /icon-16.png, 24: /icon-24.png, ... } / meta namemanifest.open_at_install contenttrue|false / meta namemanifest.browser_style contenttrue|false / !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /html在 Chrome 中侧边栏使用side_panelAPI在 Firefox 中则使用sidebar_actionAPI。WXT 会根据目标浏览器自动生成对应的 manifest 字段。wxt-demo 中的 sidepanel.html 可作参考。Unlisted CSS文件名模式{name}.(css|scss|sass|less|styl|stylus)→ 输出为{name}.css{name}/index.(css|scss|sass|less|styl|stylus)→ 输出为{name}.csscontent.(css|scss|sass|less|styl|stylus)→ 输出为content-scripts/content.csscontent/index.(css|scss|sass|less|styl|stylus)→ 输出为content-scripts/content.css{name}.content.(css|scss|sass|less|styl|stylus)→ 输出为content-scripts/{name}.css{name}.content/index.(css|scss|sass|less|styl|stylus)→ 输出为content-scripts/{name}.cssbody { /* ... */ }CSS 入口点始终是 unlisted不会进入 manifest 的content_scripts声明。如需为内容脚本注入 CSS请参考 Content Scripts 文档中的 CSS 章节。使用 SCSS、Less 等预处理器时按 Vite 官方指南配置对应预处理器依赖即可WXT 无需额外配置。wxt-demo 中 injected.content/index.css、example-2.scss 都是 CSS 入口点的实例。Unlisted Pages文件名模式{name}.html→ 输出为{name}.html{name}/index.html→ 输出为{name}.html!doctype html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleTitle/title !-- 设置在部分浏览器构建中是否移除该页面 -- meta namemanifest.include content[chrome, ...] / meta namemanifest.exclude content[chrome, ...] / /head body !-- ... -- /body /html运行时unlisted pages 通过/{name}.html路径访问const url browser.runtime.getURL(/{name}.html); console.log(url); // chrome-extension://{id}/{name}.html window.open(url); // 在新标签页打开该页面这非常适合安装后欢迎页之类的场景。注意{name}需要替换成你实际的文件名例如welcome.html。Unlisted Scripts文件名模式{name}.[jt]sx?→ 输出为{name}.js{name}/index.[jt]sx?→ 输出为{name}.js最小写法export default defineUnlistedScript(() { // 脚本加载时执行 });带选项的写法export default defineUnlistedScript({ // 设置在部分浏览器构建中是否移除该脚本 include: undefined | string[], exclude: undefined | string[], main() { // 脚本加载时执行 }, });运行时unlisted scripts 通过/{name}.js路径访问const url browser.runtime.getURL(/{name}.js); console.log(url); // chrome-extension://{id}/{name}.js你需要在需要的地方自行加载/运行这些脚本。如果脚本要注入到网页中如内容脚本向主世界注入别忘了把脚本及其相关资源加入 manifest 的web_accessible_resources否则网页环境无法访问它们。与其他 JS 入口点一致构建时该文件会在 Node.js 环境被导入因此运行时代码必须放在main内document.querySelectorAll(a).forEach((anchor) { // ✗ 错误顶层代码 // ... }); export default defineUnlistedScript(() { document.querySelectorAll(a).forEach((anchor) { // ✓ 正确 // ... }); });defineUnlistedScript的实现与defineBackground完全对称见 define-unlisted-script.ts函数参数会被包装成{ main: fn }。wxt-demo 中的 unlisted.ts 是实际示例。构建时的校验与跳过机制源码级从 find-entrypoints.ts 可以还原 WXT 构建入口点的完整流程发现用tinyglobby按PATH_GLOB_TO_TYPE_MAP的键在entrypoints/下做 glob 匹配得到候选文件列表去重如果{name}/index.{ext}与{name}/index.html同时存在非 HTML 的 index 文件会被过滤掉校验preventNoEntrypoints在目录为空时报错No entrypoints found in ...preventDuplicateEntrypointNames在出现同名入口点时抛出 Multiple entrypoints with the same name detected同名可能来自popup.html与popup/index.html这类冲突见 find-entrypoints.ts读取选项HTML 入口点走importHtmlEntrypoint解析meta与titleJS 入口点通过wxt.builder.importEntrypoints在构建器Vite中导入并读取默认导出CSS 入口点没有选项组装按类型分别调用getPopupEntrypoint、getBackgroundEntrypoint、getContentScriptEntrypoint等工厂函数生成带输出目录与规范化选项的入口点对象内容脚本的产物统一输出到content-scripts/子目录dev 兜底在wxt serve开发模式下如果没有 background 入口点会自动注入一个 noop空操作background保证扩展能正常加载见 find-entrypoints.ts跳过判定isEntrypointSkipped依据include/exclude选项判断当前目标浏览器是否应跳过该入口点——注意include与exclude不能同时使用同时声明会打印警告并将该入口点标记为跳过见 find-entrypoints.ts。这也是文档中所有类型都提供include/excludemeta 选项的原因同一份代码可以按[chrome]、[firefox]等目标浏览器裁剪构建产物。总结WXT 的 entrypoints 机制把文件系统即配置的思想贯彻到了扩展开发中目录约定决定了入口点的类型与产物路径零层或一层的文件布局、{name}.{type}的命名后缀.content、.sandbox、.sidepanel等是关键选项内联让 manifest 与入口点代码同处一地JS 入口点用defineBackground/defineContentScript/defineUnlistedScript声明HTML 入口点用manifest.*前缀的meta标签声明并天然支持按浏览器差异化自动生成 manifest让多浏览器构建Chrome、Firefox、Safari 等无需手工维护清单字段配合include/exclude还能精准裁剪每个目标平台的入口点集合。掌握了这份指南你就能从写文件开始完整地定义扩展的每个功能面——后台、弹窗、选项页、内容脚本、覆盖页与各类辅助脚本——剩下的打包与清单生成全部交给 WXT 完成。继续深入可以阅读 project-structure 了解目录全貌或通过 entrypoint-loaders 理解入口点的加载时序。赞分享前端开发工具构建工具插件系统【免费下载链接】wxt⚡ Next-gen Web Extension Framework项目地址https://gitcode.com/gh_mirrors/wx/wxt点击查看免费下载相关推荐Renovate 的 Hermit 管理器私包凭证、Git 凭据透传与嵌套环境配置实战指南Renovate 的 Hermit 管理器私包凭证、Git 凭据透传与嵌套环境配置实战指南 导读 本文聚焦 Renovate 中用于管理 Hermit htt前端开发工具构建工具插件系统如何为vanilla-extract项目自动化生成TypeScript类型文档完整指南如何为vanilla extract项目自动化生成TypeScript类型文档完整指南 vanilla extract是一个强大的零运行时TypeScript前端开发工具vanilla-extract的TypeScript类型生成自动化类型定义vanilla extract的TypeScript类型生成自动化类型定义 你是否还在为CSS样式与TypeScript类型不同步而烦恼手动编写样式类型定义前端开发工具上一篇TypeScript Any 类型完全指南通用超类型、安全边界与替代方案The Concise TypeScript Book 深度解读下一篇如何快速掌握小红书批量下载神器XHS-Downloader完整使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
