关于 build-chrome-extension
用于构建、调试、验证和打包 Chrome Manifest V3 扩展的 Skill。重点涵盖服务工作线程生命周期、内容脚本隔离、最小权限和 Web Store 准备。
主要功能
- 01Manifest V3 扩展工作流
- 02服务工作线程生命周期指引
- 03内容脚本隔离模式
- 04最小权限规划
- 05构建验证与打包
适用场景
- 01搭建新的 Chrome 扩展
- 02将扩展从 MV2 迁移到 MV3
- 03排查服务工作线程或消息问题
- 04准备 Web Store 审核构建
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yigitkonur/ skills-by-yigitkonur --skill build-chrome-extension安装后在 Claude Code 中输入/build-chrome-extension确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdagents/openai.yamlreferences/apis/core-apis.mdmessaging.mdstorage.mdreferences/frameworks/comparison.mdreferences/patterns/content-scripts.mddesktop-ipc.mdservice-worker.mdui-surfaces.mdreferences/publishing/web-store.mdreferences/testing/debugging.mdtesting-guide.mdscripts/check-mv3-manifest.mdcheck-mv3-manifest.shpreflight-extension.mdpreflight-extension.sh详细介绍
构建 Chrome 扩展
构建、调试、打包并发布 Chrome Manifest V3 扩展。重点优化服务工作线程在重启后安全运行、内容脚本隔离、最小权限原则,以及做好通过 Web Store 审核的准备。
适用场景
遇到以下任一需求时触发:
- 创建新的 Chrome MV3 扩展,或使用框架(WXT、Plasmo、CRXJS、Vite)搭建项目
- 编辑或生成
manifest.json,其中包含manifest_version: 3、service_worker、content_scripts、host_permissions、action或side_panel字段 - 实现或调试
chrome.runtime、chrome.storage、chrome.alarms、chrome.scripting、chrome.tabs、chrome.declarativeNetRequest、chrome.sidePanel或chrome.offscreenAPI - 将 Chrome 扩展连接到桌面应用或本地守护进程(Raycast Companion、本地 WebSocket 服务、Native Messaging 主机)
- 修复 MV3 服务工作线程生命周期问题:空闲终止、在顶层注册监听器、异步
onMessage返回值、chrome.alarms与setInterval的取舍 - 在 ISOLATED 和 MAIN 世界之间桥接内容脚本,或通过服务工作线程路由 fetch 请求,以使用跨域主机权限
- 将 MV2 扩展迁移到 MV3(后台页面 → 服务工作线程、阻塞式
webRequest→declarativeNetRequest、browser_action → action) - 验证已构建的
dist/或.output/chrome-mv3/文件夹、打包 Web Store zip、撰写隐私和权限说明,或准备审核备注 - 为新项目选择或比较 Chrome MV3 框架
以下情况请勿使用此技能:
- 目标平台是 Firefox、Safari,或跨浏览器 polyfill(
browser.*命名空间、web-ext工具、Safari App Extensions) - 需求是在扩展环境之外进行通用浏览器自动化——请转至
run-agent-browser - 发布普通 npm 包或库——请转至
publish-npm-package - 构建其他平台的扩展(Raycast 脚本命令、VS Code 扩展、Edge 企业策略包)——请转至对应平台的技能
固定默认项——生成代码前应用
| 键 | 默认值 |
|---|---|
| Manifest 目标 | 仅使用 manifest_version: 3;绝不生成 MV2 |
| 全新项目框架 | 使用 WXT,除非仓库已选用其他 MV3 工具 |
| 持久状态 | 使用 chrome.storage.local;临时且需在重启后安全保留的状态使用 chrome.storage.session |
| 后台模型 | 事件驱动的服务工作线程,监听器在顶层同步注册 |
| 定期工作 | 使用 chrome.alarms——后台调度绝不使用 setTimeout/setInterval |
| 网络修改 | 使用 chrome.declarativeNetRequest——绝不使用阻塞式 webRequest |
| 权限 | 遵循最小权限原则;优先通过 chrome.permissions.request 授予 optional_permissions 和 optional_host_permissions |
| 加载的文件夹 | 仅加载构建产物:WXT .output/chrome-mv3-dev/ 或 .output/chrome-mv3/;Plasmo build/chrome-mv3-*;CRXJS/Vite dist/ |
| 桌面 IPC 传输 | 无特权守护进程使用本地 WebSocket;需要安装程序的系统工具使用 Native Messaging |
| 打包前检查 | 压缩前,针对生产构建运行 scripts/check-mv3-manifest.sh 和 scripts/preflight-extension.sh |
MV3 易踩问题 — 请牢记
这些故障在每次 MV3 构建中都会反复出现。编写代码前,请牢记:
- Service worker 会在空闲时退出(默认约 30 秒)。模块作用域的全局状态会在事件之间消失。每次
await前持久化,并在事件入口处重新加载。 - 在顶层同步注册
chrome.runtime.onMessage、onInstalled、onStartup、alarm 和 tab 监听器。延迟注册的监听器会错过唤醒事件。 setTimeout/setInterval无法让 service worker 保持运行,也无法保证在空闲周期之间可靠触发。请使用chrome.alarms.create,并将periodInMinutes设为>= 0.5。- 在 Chrome 148 及更高版本中,
chrome.runtime.onMessage监听器原生支持返回Promise或使用async。为保持向后兼容(<148 且跨浏览器),异步处理程序必须同步return true,并在完成时调用sendResponse;否则消息通道会立即关闭,发送方收到undefined(或因 "The message port closed before a response was received" 而被拒绝)。 - Service worker 中的本地 WebSocket 连接在空闲约 30 秒后会终止。必须维持活跃的心跳 ping(间隔 <30 秒,例如 24 秒),以重置 Chrome 116 及更高版本的空闲计时器。空闲的开放套接字无法让 worker 保持运行。
- 监听 127.0.0.1 的本地 WebSocket 容易受到恶意网页发起的跨站 WebSocket 劫持(CSWSH)。守护进程必须根据
chrome-extension://<id>验证Origin标头,并在 HTTP Upgrade 期间要求提供身份验证令牌。 - Native Messaging 主机绝不能向标准输出(
stdout)写入调试日志或未格式化的输出。任何非帧格式的标准输出都会破坏 32 位长度前缀并导致连接崩溃。日志应发送到stderr。 - Native Messaging 主机发送给 Chrome 的消息严格限制为 1 MB。超过 1 MB 会立即终止进程。
- Content script 默认运行在隔离世界中。页面的 JS、框架和
window.*全局变量都不可见。仅在需要访问页面 JS 时使用world: "MAIN",随后通过postMessage和同源令牌建立桥接。 - 扩展来源的
fetch需要匹配的host_permissions。Content script 的fetch受页面来源和 CORS 规则约束;请通过chrome.runtime.sendMessage将需要特权的请求路由到 service worker。 - MV3 CSP 禁止扩展页面中的内联
<script>、eval()、new Function()和远程可执行代码。请打包所有内容;不要从 CDN 加载脚本。(manifest.sandbox中的沙盒页面是唯一例外,允许使用unsafe-eval。) - 手写的
manifest.json路径必须指向构建产物(例如background.js、content.js、popup.html),不能指向src/*.ts或未构建的源文件。 - 安装时申请
<all_urls>或广泛的主机权限会增加 Web Store 审核阻力。优先使用activeTab加上可选的主机授权。
决策规则
| 决策 | 默认方案 | 在以下情况升级处理 |
|---|---|---|
| 后台状态 | chrome.storage.session/local | 事务、大型索引、二进制数据 → IndexedDB |
| 定期工作 | chrome.alarms | 实时流 → 设计重连机制和队列 |
| 网络修改 | declarativeNetRequest | 只读观察 → 使用非阻塞的 webRequest |
| 获取页面数据 | 服务工作线程通过 fetch 配合主机权限 | 页面源足以满足需求 → 内容脚本通过 fetch 获取 |
| 访问页面 JS | 使用 world: "MAIN" 桥接,并验证负载 | 仅访问 DOM → 保持在 ISOLATED world |
| UI 状态持久化 | chrome.storage.session | 必须在浏览器重启后保留 → 使用 local 或 sync |
| 主机访问权限 | activeTab 或可选主机授权 | 若没有安装时授予的主机访问权限,扩展将无法运行 |
| 侧边面板还是弹出窗口 | 持续显示的配套 UI 使用侧边面板 | 快速操作或简短表单 → 使用弹出窗口 |
| 离屏文档 | 从服务工作线程访问 DOM/canvas/clipboard/audio/Worker | 若弹出窗口、选项页或内容脚本可以处理 → 跳过 |
| 桌面通信 | 本地 WebSocket(ws://127.0.0.1:<port>),并防范 CSWSH | 深度操作系统集成/企业分发/不暴露本地端口 → 使用 chrome.runtime.connectNative |
路由边界
| 请求类型 | 操作 |
|---|---|
| "Build a Chrome extension" | 使用此技能;目标为 MV3 |
| "Build for Chrome and Firefox" | 在此保留 Chrome MV3;跨浏览器层不在范围内 |
| "Automate a website in a browser" | 路由至 run-agent-browser |
| "Write Playwright tests for an extension" | 此处提供扩展启动/加载说明;本工具包不涵盖更广泛的 Playwright 编写 |
| "Publish to npm" | 路由至 publish-npm-package |
| "Submit to Chrome Web Store" | 使用此技能;阅读 references/publishing/web-store.md |
| "Deploy via enterprise policy" | 不在范围内,除非存在专门的企业技能 |
工作流
1. 确定扩展范围
询问:「扩展包含哪些界面,需要哪些 Chrome API,哪些权限可以延后到运行时申请?」
- 列出所需界面:弹出窗口、选项页、侧边面板、内容脚本、服务工作线程、离屏文档、开发者工具。
- 列出所需的 Chrome API 及相应权限。
- 区分安装时权限与
optional_permissions/optional_host_permissions。 - 选择框架——参见
references/frameworks/comparison.md。 - 记录 Chrome 将加载的构建输出文件夹。
2. 搭建脚手架或改造现有项目
询问:「这是从零开始、基于现有框架构建,还是手写 manifest?」
- 从零开始时默认使用:WXT。
- 将现有 Vite 应用改造成扩展:CRXJS。
- 自定义构建流程/需要明确控制:vanilla + Vite。
- 现有仓库:遵循其框架和输出约定。
Chrome 必须加载构建输出文件夹,绝不能加载 src/ 或未打包的 TypeScript。
3. 实现扩展上下文
询问:「由哪个上下文管理状态,哪个上下文管理 DOM,哪些消息会跨越边界?」
编写代码前阅读对应的参考资料:
- 服务工作线程生命周期、持久化、定时任务、离屏文档 →
references/patterns/service-worker.md - 内容脚本、世界隔离、幂等性、跨源路由 →
references/patterns/content-scripts.md - 弹出窗口、选项页、侧边面板、开发者工具、新标签页界面 →
references/patterns/ui-surfaces.md - 一次性消息、端口、外部/原生消息传递、页面桥接 →
references/apis/messaging.md - 桌面通信、Raycast Companion、原生消息传递 →
references/patterns/desktop-ipc.md chrome.storage区域、配额、类型化封装、迁移 →references/apis/storage.md- 标签页、脚本、定时任务、DNR、侧边面板、离屏文档、运行时 →
references/apis/core-apis.md - Manifest、权限和 MV2 迁移决策,参见此
SKILL.md中锁定的默认设置和防护规则。
验证每个跨边界负载:消息、存储读取结果、外部 API 响应、MAIN world 桥接数据。
4. 验证构建产物
请问:“Chrome 能加载这个确切的文件夹吗?其中的所有清单路径都存在吗?”
在技能目录中,针对你的构建文件夹运行:
scripts/check-mv3-manifest.sh dist
scripts/preflight-extension.sh dist
根据框架调整路径:
| 框架 | 开发输出 | 生产输出 |
|---|---|---|
| WXT | .output/chrome-mv3-dev/ | .output/chrome-mv3/ |
| Plasmo | build/chrome-mv3-dev/ | build/chrome-mv3-prod/ |
| CRXJS | dist/ | dist/ |
| Vanilla Vite | dist/ | dist/ |
修改任一脚本前,先阅读 scripts/check-mv3-manifest.md 和 scripts/preflight-extension.md。
5. 测试扩展行为
请问:“有什么故障只有在 Chrome 加载扩展后才会出现?”
- 对纯逻辑和消息/存储辅助函数进行单元测试;只在边界处模拟
chrome.*。 - 通过
chrome://extensions→“加载已解压的扩展程序”手动加载构建产物。 - 从扩展程序页面检查 Service Worker;触发一次休眠/唤醒周期。
- 在允许和禁止的 URL 上测试内容脚本注入。
- 在调用主机之前,验证运行时权限请求的 UX(
chrome.permissions.request)。
扩展专用测试请阅读 references/testing/testing-guide.md;Service Worker、弹出窗口、内容脚本、权限和存储的调试请阅读 references/testing/debugging.md。
6. 打包以供 Web Store 审核
请问:“审核者能否各用一句话说明唯一用途、每项权限的必要性、数据使用方式和远程代码策略?”
- 构建生产产物。
- 针对该生产构建运行两个打包脚本。
- 压缩前清理无关内容:源映射(除非有意随包发布)、测试、
.DS_Store、__MACOSX、框架缓存。 - 在构建产物文件夹内创建 zip(确保
manifest.json位于 zip 根目录)。 - 准备:隐私实践、逐项权限说明、远程代码披露、数据使用认证、审核者测试说明。
- 提交前阅读
references/publishing/web-store.md。
最低构建证据
停止前,根据任务匹配相应证据:
| 任务 | 证据 |
|---|---|
| 新建脚手架 | 构建产物路径 + check-mv3-manifest.sh 结果 |
| 功能变更 | 相关单元/集成测试 + Chrome 行为发生变化时的手动加载说明 |
| 清单/权限变更 | check-mv3-manifest.sh 结果 + 权限说明 |
| 内容脚本变更 | 允许的 URL 注入结果 + 禁止的 URL 注入结果 |
| Service Worker 变更 | 重启恢复说明,或明确注明“未测试” |
| 打包/Web Store 工作 | preflight-extension.sh 结果 + zip 路径 + 审核者说明 |
输出要求
最终报告包括:
- 已加载的构建目录
- zip 路径(如已打包)
- 手动测试时使用的 Chrome 版本
- 权限摘要及主机权限说明
- Web Store 政策合规情况:唯一用途、数据使用、远程代码、MV3 合规性
- 运行过的脚本/测试,包括失败项
- 剩余手动检查的审核者说明
防护原则
- 新项目中绝不生成或保留 Manifest V2。
- 除非框架明确在
src/中生成可加载代码,否则绝不在 Chrome 中加载src/。 - 绝不在 Service Worker 的模块级全局变量中保存持久状态。
- 绝不依赖
setTimeout/setInterval保持 Service Worker 运行。 - 对常规 MV3 网络修改,绝不使用阻塞式
webRequest。 - 未提供功能级理由且未记录更窄的替代方案时,绝不请求
<all_urls>。 - 绝不通过未经身份验证的内容脚本消息开放任意后台
fetch。 - 绝不发布远程可执行代码或未声明的远程代码行为。
- 声称完成前,始终根据
manifest.json验证构建产物路径。 - 始终确保每项权限都能用一句话说明理由。
总结
MV3 会反复惩罚同样的错误:服务工作线程状态丢失、监听器注册过晚、基于计时器的调度、隔离世界带来的意外、权限范围过宽,以及发布 src/ 而非构建产物。应用固定的默认设置,深入了解时参阅相关资料,针对构建文件夹运行内置脚本,并提供可供审核的证据。