关于 chrome-extension
面向 Chrome 扩展开发者的 Manifest V3 参考型 Skill,涵盖构建、调试和发布。它按任务路由到相关资料,帮助选择执行上下文与实现方式。
主要功能
- 01Manifest V3 配置与权限
- 02Service worker 生命周期指南
- 03内容脚本与消息传递模式
- 04存储、网络和界面参考
- 05调试与发布指南
适用场景
- 01从头创建 Manifest V3 扩展
- 02为新功能选择执行上下文
- 03排查消息传递或 Service worker 问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samber/ cc-skills --skill chrome-extension安装后在 Claude Code 中输入/chrome-extension确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/content-scripts.mddebugging-mistakes.mdexecution-contexts.mdmanifest-v3.mdmessaging-rpc.mdnetwork-csp.mdpermissions.mdpublishing.mdservice-worker.mdstorage.mdtypescript-build.mdui-surfaces.mdweb-accessible-resources.md详细介绍
Chrome 扩展开发(Manifest V3)
本技能涵盖使用 MV3 构建、调试和发布 Chrome 扩展所需的一切内容。本文档是一份路由指南:请先阅读本文,了解架构和决策要点,然后加载相关参考文件,查看实现细节。
参考文件
只阅读与当前任务相关的参考文件。每个文件都是独立完整的。
| 文件 | 何时阅读 |
|---|---|
references/manifest-v3.md | 设置或修改 manifest.json、配置图标、版本管理 |
references/service-worker.md | 后台逻辑、生命周期、状态持久化、闹钟、事件 |
references/content-scripts.md | 向页面注入代码、隔离世界/主世界、动态注入、SPA 处理、孤立问题 |
references/messaging-rpc.md | 任意上下文之间的通信、类型化协议、RPC 层、异步处理程序模式 |
references/ui-surfaces.md | 弹出窗口、选项页面、侧边栏、上下文菜单、命令、通知、地址栏、开发者工具面板 |
references/storage.md | chrome.storage(local/sync/session)、配额、响应式模式、框架钩子 |
references/network-csp.md | 内容脚本发起的 HTTP 请求、CSP 绕过中继、declarativeNetRequest、离屏文档、CORS |
references/permissions.md | 必需/可选权限、主机权限、activeTab、运行时请求流程 |
references/web-accessible-resources.md | 向网页公开扩展文件、安全影响 |
references/typescript-build.md | TypeScript 设置、项目结构、构建工具比较、打包 |
references/publishing.md | Chrome Web Store 提交、审核流程、拒绝原因、更新、隐私政策 |
references/execution-contexts.md | 通信流程图、各上下文的能力/限制、选择合适的消息传递方式 |
references/debugging-mistakes.md | 扩展的 DevTools、测试 SW 终止、常见陷阱、错误模式 |
架构概览
Chrome 扩展最多包含 5 个通过消息传递进行通信的执行上下文:
┌──────────────────────────────────────────────────────────┐
│ Extension Process │
│ ┌─────────────────┐ ┌───────┐ ┌─────────┐ ┌──────┐ │
│ │ Service Worker │ │ Popup │ │ Options │ │ Side │ │
│ │ (background) │ │ │ │ Page │ │Panel │ │
│ │ - No DOM │ │ Full │ │ Full │ │ Full │ │
│ │ - Ephemeral │ │ DOM │ │ DOM │ │ DOM │ │
│ │ - All chrome.* │ │ All │ │ All │ │ All │ │
│ │ APIs │ │ APIs │ │ APIs │ │ APIs │ │
│ └────────┬─────────┘ └───┬───┘ └────┬────┘ └──┬───┘ │
│ │ chrome.runtime.sendMessage / connect │ │
└───────────┼────────────────┼───────────┼──────────┼──────┘
│ │ │ │
chrome.tabs.sendMessage │ │ │
│ │ │ │
┌───────────┼────────────────┼───────────┼──────────┼──────┐
│ Web Page ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Content Script │ │ Main World Script │ │
│ │ (isolated world) │◄──►│ (page context) │ │
│ │ - Shared DOM │ │ - Shared DOM │ │
│ │ - Own JS scope │ │ - Page JS scope │ │
│ │ - chrome.runtime │ │ - No chrome.* API │ │
│ │ - chrome.storage │ │ - Full page access│ │
│ │ - Subject to CSP │ │ - Subject to CSP │ │
│ │ (network only) │ │ (fully) │ │
│ └──────────────────┘ └──────────────────┘ │
│ ▲ window.postMessage │
│ │ (through shared DOM) │
└──────────────────────────────────────────────────────────┘
通信流程(带标签的通道)
┌───────────────────────────────────────────────────────────────────────────┐
│ Extension Process │
│ │
│ ┌─────────────────┐ chrome.runtime ┌───────┐ ┌─────────┐ ┌──────┐ │
│ │ Service Worker │◄─.sendMessage()──│ Popup │ │ Options │ │ Side │ │
│ │ (background) │◄─.connect()──────│ │ │ Page │ │Panel │ │
│ │ │ └───────┘ └─────────┘ └──────┘ │
│ │ - No DOM │ ┌────────────────────────────────────────────┐ │
│ │ - Ephemeral 30s │ │ SW cannot push to these pages. │ │
│ │ - All chrome.* │ │ Use: ports (.connect) or storage.onChanged │ │
│ └────────┬─────────┘ └────────────────────────────────────────────┘ │
│ │ │
│ chrome.storage.onChanged ◄── fires across ALL contexts simultaneously │
│ │
└───────────┼──────────────────────────────────────────────────────────────┘
│ chrome.tabs.sendMessage(tabId, ...) [SW must know tabId]
│
┌───────────┼──────────────────────────────────────────────────────────────┐
│ Web Page ▼ │
│ ┌──────────────────┐ window.postMessage ┌──────────────────┐ │
│ │ Content Script │◄───────────────────►│ Main World Script │ │
│ │ (isolated world) │ Custom DOM events │ (page context) │ │
│ │ │ │ │ │
│ │ chrome.runtime ───┼── to/from SW │ No chrome.* APIs │ │
│ │ chrome.storage │ │ Full page JS │ │
│ │ Shared DOM │ │ Shared DOM │ │
│ │ Page CSP (network)│ │ Page CSP (full) │ │
│ └──────────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
有关详细流程图(三层桥接、跨扩展、存储广播)以及各上下文的权限、限制和变通方法:→ 阅读 references/execution-contexts.md
通信方式一览
| 方式 | 方向 | 最适用于 |
|---|---|---|
chrome.runtime.sendMessage | 任意扩展上下文 → SW | 一次性请求/响应(占 90% 的情况) |
chrome.tabs.sendMessage | SW → 内容脚本(按 tabId) | 向特定标签页推送数据 |
chrome.runtime.connect (Port) | 双向 | 流式传输、进度、SW ↔ 弹出窗口 |
window.postMessage | 同一页面上的不同世界之间 | 页面 JS ↔ 内容脚本桥接 |
chrome.storage.onChanged | 广播到所有上下文 | 同步设置,无需消息传递 |
→ 完整矩阵及限制和边缘情况:references/execution-contexts.md → 实现模式、类型化协议、RPC 层:references/messaging-rpc.md
核心架构规则
-
Service worker 是临时的。 空闲 30 秒后会终止。所有状态都必须持久化到 chrome.storage。所有事件监听器都必须在顶层同步注册。除非只延迟几秒,否则绝不要使用 setTimeout/setInterval。→ 阅读
references/service-worker.md -
Content script 在页面的来源中运行。 Content script 发出的网络请求受页面的 CSP 和 CORS 限制。要绕过这些限制,请通过 service worker 中转。→ 阅读
references/network-csp.md -
消息传递是核心。 所有跨上下文交互都使用 chrome.runtime 消息传递。最常见的错误:忘记在异步消息监听器中
return true。→ 阅读references/messaging-rpc.md -
权限决定 CWS 审核速度。 宽泛的 host_permissions 会触发人工审核(数周)。使用 activeTab + 可选权限可快速通过自动审核。→ 阅读
references/permissions.md -
Popup 失去焦点时会被销毁。 Side panel 会持续存在。请根据交互时长选择。→ 阅读
references/ui-surfaces.md
决策树:不同任务应由哪个上下文处理?
“我需要在用户访问页面时运行代码”
→ 使用 Content script。对于已知 URL 模式,使用静态方式(manifest);对于用户触发的注入,使用动态方式(chrome.scripting)。除非需要访问页面 JS,否则默认使用隔离世界。→ 阅读 references/content-scripts.md
“我需要向自己的 API 发起 HTTP 请求”
- 从 popup/options/side panel 发起:直接使用 fetch() 即可(扩展来源,不受 CSP 限制)
- 从 CSP 严格的页面中的 content script 发起:通过 service worker 中转
- 从 service worker 发起:直接使用 fetch() 即可(需要针对目标域名配置 host_permissions)→ 阅读
references/network-csp.md
“我需要存储用户设置”
- 需要跨设备同步的设置:chrome.storage.sync(上限 100KB)
- 大型数据或缓存:chrome.storage.local(10MB,或在获得权限后不设上限)
- 需要在 SW 重启后保留的临时状态:chrome.storage.session → 阅读
references/storage.md
“我需要修改 HTTP 标头或拦截请求”
→ 使用 declarativeNetRequest(不要使用 webRequest,MV3 中已移除其拦截功能)→ 阅读 references/network-csp.md
“我需要让页面的 JavaScript 与扩展通信”
→ 使用三层桥接:页面(window.postMessage)→ content script → service worker → 阅读 references/messaging-rpc.md
“我需要了解各个上下文能做什么、不能做什么”
→ 阅读 references/execution-contexts.md — 其中的各上下文卡片列出了 chrome.* 访问权限、DOM、网络、存储、生命周期、硬性限制和实用变通方法。
“我需要定期执行后台任务”
→ 使用 chrome.alarms(最短间隔为 30 秒)。不要使用 setTimeout。→ 阅读 references/service-worker.md
“我需要在后台使用 DOM API”(DOMParser、Canvas、Audio)
→ 使用 Offscreen document。每个扩展只能有一个,且只能使用 chrome.runtime。→ 阅读 references/network-csp.md
“我需要通过 OAuth 进行身份验证”
→ 使用 chrome.identity.launchWebAuthFlow() 或 chrome.identity.getAuthToken()(仅限 Google)→ 阅读 references/service-worker.md(identity 部分)
工作流:从零开始创建扩展
-
定义 manifest,并使用最少权限。从
activeTab+scripting开始。→ 阅读references/manifest-v3.md -
设置 TypeScript 和构建工具(或使用 CRXJS 进行基于 Vite 的开发)。→ 阅读
references/typescript-build.md -
实现 service worker,并在顶层注册所有事件监听器。→ 阅读
references/service-worker.md -
如果需要与页面交互,添加 content script。 → 阅读
references/content-scripts.md -
按需构建 UI 界面(popup、options、side panel)。→ 阅读
references/ui-surfaces.md -
配置所有上下文之间的消息传递。 → 阅读
references/messaging-rpc.md -
使用 DevTools 测试,尤其要测试 service worker 终止的情况。 → 阅读
references/debugging-mistakes.md -
发布到 Chrome Web Store。 → 阅读
references/publishing.md
工作流:向现有扩展添加功能
- 确定该功能所属的上下文(参见上方决策树)。
- 阅读该上下文的相关参考文件。
- 检查是否需要新权限。新增功能优先使用 optional_permissions。→ 阅读
references/permissions.md - 如果要添加新的内容脚本、UI 界面或权限,请更新清单。
- 妥善处理扩展更新(内容脚本孤立)。→ 阅读
references/content-scripts.md(孤立部分)
最小 manifest.json 模板
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0.0",
"description": "What it does in one sentence",
"permissions": ["storage", "activeTab", "scripting"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"background": {
"service_worker": "background.js",
"type": "module"
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
→ 如需查看包含所有字段的完整清单参考,请参阅:references/manifest-v3.md
代码模式速查
异步消息处理程序(安全模式)
// Wrap async handlers to avoid the return-true trap
function asyncHandler(
fn: (msg: any, sender: chrome.runtime.MessageSender) => Promise<any>,
) {
return (
message: any,
sender: chrome.runtime.MessageSender,
sendResponse: (r: any) => void,
) => {
fn(message, sender)
.then(sendResponse)
.catch((e) => sendResponse({ __error: true, message: e.message }));
return true; // literal true, not Promise<true>
};
}
chrome.runtime.onMessage.addListener(
asyncHandler(async (msg, sender) => {
if (msg.type === "FETCH") {
const res = await fetch(msg.url);
return { ok: res.ok, data: await res.text() };
}
}),
);
CSP 绕过中继(内容脚本 → 服务工作线程 → API)
// content-script.ts
async function apiCall(endpoint: string, options?: RequestInit) {
return chrome.runtime.sendMessage({ type: "API_RELAY", endpoint, options });
}
// background.ts
const ALLOWED_ENDPOINTS = ["https://api.example.com"];
chrome.runtime.onMessage.addListener(
asyncHandler(async (msg) => {
if (msg.type !== "API_RELAY") return;
if (!ALLOWED_ENDPOINTS.some((e) => msg.endpoint.startsWith(e))) {
throw new Error("Blocked endpoint");
}
const res = await fetch(msg.endpoint, msg.options);
return { ok: res.ok, status: res.status, data: await res.text() };
}),
);
在服务工作线程重启后保留状态
// Use chrome.storage.session for ephemeral state
chrome.storage.session.setAccessLevel({
accessLevel: "TRUSTED_AND_UNTRUSTED_CONTEXTS",
});
async function getState<T>(key: string, fallback: T): Promise<T> {
const result = await chrome.storage.session.get(key);
return result[key] ?? fallback;
}
async function setState<T>(key: string, value: T): Promise<void> {
await chrome.storage.session.set({ [key]: value });
}
孤立内容脚本检测
function isExtensionContextValid(): boolean {
try {
return !!chrome.runtime?.id;
} catch {
return false;
}
}
// Before any chrome.runtime call
if (!isExtensionContextValid()) {
showRefreshBanner();
return;
}
禁止事项
- 不要使用
eval()、new Function()或加载远程脚本。MV3 禁止这些行为。 - 不要在服务工作线程中使用超过 5 秒的
setTimeout/setInterval。 - 不要在回调或异步函数中注册事件监听器。
- 除非绝对必要,否则不要使用
<all_urls>主机权限。 - 测试时不要依赖 DevTools 让服务工作线程保持运行。
- 不要忘记在异步消息监听器中添加
return true。 - 不要在服务工作线程中使用
localStorage或sessionStorage(这些 API 在其中不存在)。 - 不要假设内容脚本在扩展更新后仍会继续运行。
- 不要使用
webRequest阻止功能(已在 MV3 中移除)。请使用declarativeNetRequest。 - 不要使用
chrome.extension.getBackgroundPage()(已在 MV3 中移除)。