mcpField
C
chrome-extensions

chrome-extensions

github.com/googlechrome开发2.6k11.2k 次安装2 天前更新
代码仓库

关于 chrome-extensions

Chrome Extensions 是面向开发者的技能,用于构建 Manifest V3 扩展并准备发布到 Chrome Web Store。它整理了扩展组件、权限、API 和发布环节的实现规则与常见问题。

主要功能

  1. 01Manifest V3 实现指南
  2. 02图标与清单检查
  3. 03侧边栏设置模式
  4. 04符合 CSP 的代码执行指南
  5. 05权限与 API 常见问题
  6. 06Service Worker 与捕获指南

适用场景

  1. 01构建 Manifest V3 扩展
  2. 02添加弹出窗口或侧边栏
  3. 03实现扩展认证或捕获功能
  4. 04准备将扩展发布到 Chrome Web Store

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/googlechrome/modern-web-guidance --skill chrome-extensions

安装后在 Claude Code 中输入/chrome-extensions确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/extensions/
api-calling.md
auth-identity.md
content-scripts.md
context-menus.md
csp-sandbox.md
declarative-net-request.md
devtools.md
icons.md
media-capture.md
message-passing.md
omnibox.md
permissions.md
popup-ui.md
prompt-api.md
service-worker.md
side-panel.md
storage.md
tab-management.md
user-scripts.md
references/webstore/
chromewebstore-template.md
privacy-policy.md
review-checklist.md
store-listing.md

详细介绍

Chrome 扩展程序

使用 Manifest V3 构建可用于生产环境的 Chrome 扩展程序,并发布到 Chrome Web Store。

第 1 部分 — 构建扩展程序

强制规则

以下是导致扩展程序损坏的最常见原因。违反任何一条都会导致构建无法正常运行。

1. 图标:只引用已创建的文件,或者完全省略图标
❌ BROKEN — referencing files that don't exist or reusing one file for all sizes:
   "icons": { "16": "icon.png", "48": "icon.png", "128": "icon.png" }

✅ CORRECT — each size is a separate file at the correct pixel dimensions:
   "icons": { "16": "icons/icon-16.png", "48": "icons/icon-48.png", "128": "icons/icon-128.png" }
   (where icon-16.png is 16×16px, icon-48.png is 48×48px, icon-128.png is 128×128px)

✅ ALSO CORRECT — omit icons from manifest if you cannot generate real PNG files:
   (just remove the "icons" and "default_icon" fields — Chrome uses a default icon)

如果包含图标引用,就必须创建对应的图像文件。 使用脚本生成图像(参见 references/extensions/icons.md),或者省略图标。切勿引用不存在的文件。

2. 侧边栏:必须提供打开方式

定义 "side_panel": {"default_path": "..."} 并不会使其可以打开。请添加触发方式:

// In service-worker.js — open side panel on extension icon click
// IMPORTANT: chrome.action.onClicked ONLY fires when there is NO default_popup
chrome.action.onClicked.addListener(async (tab) => {
  await chrome.sidePanel.open({ windowId: tab.windowId });
});

如果扩展程序同时包含弹出窗口和侧边栏,请在弹出窗口中添加调用 chrome.sidePanel.open() 的按钮。或者,使用 chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })——但属性名称是 openPanelOnActionClick,不是 openPanelOnActionIconClick;带有“Icon”的变体会导致同步 TypeError,并静默中止服务工作线程。使用 setPanelBehavior 时,不要同时定义 default_popup。参见 references/extensions/side-panel.md。

3. 代码执行:只能使用沙盒 iframe

扩展程序的 CSP 会阻止在所有扩展程序页面中使用 eval()、new Function() 和内联 <script>。

// ❌ BROKEN — direct iframe DOM access throws SecurityError
iframe.contentDocument.write(html);

// ❌ BROKEN — eval in extension page
eval(userCode); // CSP blocks this

// ✅ OPTION A: Sandbox in manifest + postMessage
// manifest.json: { "sandbox": { "pages": ["sandbox.html"] } }
iframe.contentWindow.postMessage({ html, css, js }, '*');
// sandbox.html receives and runs:
window.addEventListener('message', (e) => { eval(e.data.js); /* allowed in sandbox */ });

// ✅ OPTION B: Blob URL (creates separate origin, bypasses extension CSP)
iframe.src = URL.createObjectURL(new Blob([doc], { type: 'text/html' }));

// ✅ OPTION C: srcdoc
iframe.srcdoc = `<style>${css}</style>${html}<script>${js}<\/script>`;

完整详情请参见 references/extensions/csp-sandbox.md。

4. tab.url 需要 tabs 权限

如果没有该权限,tab.url 会静默返回 undefined,不会抛出错误。参见 references/extensions/permissions.md。

5. 始终使用 async/await,切勿使用 .then() 链
// ❌ BAD
chrome.tabs.query({active: true, currentWindow: true}).then(tabs => {
  chrome.scripting.executeScript({target: {tabId: tabs[0].id}, files: ['content.js']}).then(() => {});
});

// ✅ GOOD
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content.js'] });

对于需要执行异步操作的 runtime.onMessage 监听器:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  (async () => {
    const data = await chrome.storage.local.get('key');
    sendResponse({ data });
  })();
  return true; // keeps channel open
});
6. 内容脚本:不要阻塞主线程

修改大量 DOM 元素时,请使用 requestAnimationFrame 分批处理,并在批次之间让出执行权:

async function highlightAll(elements) {
  const BATCH = 20;
  for (let i = 0; i < elements.length; i += BATCH) {
    await new Promise(r => requestAnimationFrame(() => {
      elements.slice(i, i + BATCH).forEach(el => el.style.backgroundColor = 'yellow');
      r();
    }));
    if (globalThis.scheduler?.yield) await scheduler.yield();
  }
}

参见 references/extensions/content-scripts.md。

7. 服务工作线程是临时的,切勿将状态存储在变量中
// ❌ BROKEN — state lost when SW terminates (~30s of inactivity)
let count = 0;
chrome.tabs.onUpdated.addListener(() => { count++; });

// ✅ CORRECT — persist in chrome.storage, read on every event
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
  if (changeInfo.status !== 'complete') return;
  const { count = 0 } = await chrome.storage.local.get('count');
  await chrome.storage.local.set({ count: count + 1 });
  await chrome.action.setBadgeText({ text: String(count + 1) });
});

请使用 chrome.alarms,而不是 setTimeout/setInterval。参见 references/extensions/service-worker.md。

8. chrome.identity:开发版和生产版的扩展程序 ID 不同

使用 Google 登录时,OAuth client_id 与特定扩展程序 ID 绑定。未打包的开发版本和 Chrome Web Store 版本的 ID 会有所不同。

如需在开发期间固定 ID,请在 manifest.json 中添加 "key" 字段:

  1. 将扩展程序打包一次(chrome://extensions → Pack)
  2. 从 .crx 中提取公钥
  3. 在 manifest.json 中添加 "key": "MIIBIjANBgkqh..."

务必注明:“发布到 Chrome Web Store 后,请使用商店分配的扩展程序 ID 更新 OAuth 客户端。”参见 references/extensions/auth-identity.md。

9. 上下文菜单:操作完成后向用户提供反馈

上下文菜单项执行操作(保存、复制等)时,请向用户确认操作已完成。使用通知、徽章闪烁或注入的提示框,不要让操作悄无声息地发生。完整的提示框实现请参见 references/extensions/context-menus.md。

10. Prompt API:可在服务工作线程、弹出窗口和侧边栏中使用

LanguageModel API 可在所有扩展程序上下文中使用——服务工作线程、弹出窗口和侧边栏均可使用,且无需额外的清单权限。扩展程序还可以使用 LanguageModel.params(),而网页无法使用此方法:

const params = await LanguageModel.params();
// { defaultTopK: 3, maxTopK: 128, defaultTemperature: 1, maxTemperature: 2 }

有关 Prompt API 的通用用法模式(可用性检查、会话创建、流式处理),请使用 modern-web-guidance 技能。扩展程序专用的接线示例请参见 references/extensions/prompt-api.md。

11. chrome.action API 需要在清单中定义 action

使用 chrome.action.setBadgeText、chrome.action.setIcon 或 chrome.action.onClicked,需要在 manifest.json 中添加 "action" 键——即使其值为空也一样。否则,chrome.action 将为 undefined。

// ❌ BROKEN — manifest has no "action" key
await chrome.action.setBadgeText({ text: '5' });
// TypeError: Cannot read properties of undefined (reading 'setBadgeText')

// ✅ FIX — add "action" to manifest.json (at minimum an empty object)
{ "action": {} }
// or with a popup:
{ "action": { "default_popup": "popup/popup.html" } }
12. activeTab 仅在用户直接触发时有效——不适用于侧边栏

activeTab 仅在用户直接触发操作(点击操作图标、选择上下文菜单项、使用键盘快捷键或选择地址栏建议)时,授予对当前标签页的临时访问权限——在侧边栏或弹出窗口内点击按钮则不行。请改用 tabs + host_permissions。参见 references/extensions/permissions.md 和 references/extensions/side-panel.md。

13. DevTools 面板 URL 相对于扩展根目录

创建 DevTools 面板时,面板 HTML 路径相对于扩展根目录,而不是相对于调用 chrome.devtools.panels.create() 的 DevTools 页面。

// ❌ BROKEN — path relative to devtools/ directory
chrome.devtools.panels.create("My Panel", "", "panel/panel.html");

// ✅ CORRECT — full path from extension root
chrome.devtools.panels.create("My Panel", "", "devtools/panel/panel.html");

参见 references/extensions/devtools.md。

14. 无痕文档无法访问大多数 chrome.* API

无痕文档(chrome.offscreen)受到严格限制。大多数 chrome.* API 均不可用,包括 chrome.downloads、chrome.tabs、chrome.action 等。

// ❌ BROKEN — chrome.downloads is undefined in offscreen documents
chrome.downloads.download({ url, filename: 'recording.webm' }); // TypeError

// ❌ BROKEN — chrome.action is undefined in offscreen documents
chrome.action.setBadgeText({ text: 'REC' }); // TypeError

无痕文档中仅可使用以下 API:

  • chrome.runtime.sendMessage / chrome.runtime.onMessage
  • chrome.runtime.getURL
  • 标准 Web API(DOM、fetch、MediaRecorder、Canvas、Web Audio 等)

经验法则: 无痕文档负责 Web API 相关工作(录制、解析、音频)。服务工作线程负责所有 chrome.* API 相关工作(下载、更新徽章、通知)。使用 chrome.runtime.sendMessage 在两者之间传递消息。参见 references/extensions/message-passing.md。

15. 通知和徽章图标必须指向真实图像文件

chrome.notifications.create() 要求有效的 iconUrl 指向实际图像文件。 如果文件不存在或路径错误,调用将失败,并显示 "Unable to download all specified images."

// ❌ BROKEN — icon file doesn't exist
chrome.notifications.create('reminder', {
  type: 'basic',
  iconUrl: 'icons/icon-128.png', // File not in extension!
  title: 'Reminder',
  message: 'Time is up!'
});

// ✅ Generate a data URL at runtime via OffscreenCanvas — no file needed.
// See `references/extensions/icons.md` for a reusable implementation.
const iconUrl = await getIconDataUrl();
chrome.notifications.create('reminder', { type: 'basic', iconUrl, title: 'Reminder', message: 'Time is up!' });

这适用于 chrome.* API 中的所有图像引用——通知、chrome.action.setIcon、上下文菜单图标等。引用的文件必须存在。

16. 标签页捕获:使用状态锁定防止重复启动

如果上一次捕获仍在进行时调用 chrome.tabCapture.getMediaStreamId(),则会失败并显示 "Cannot capture a tab with an active stream"。快速双击扩展图标很容易触发这种情况。请使用显式状态锁定:

// ❌ BROKEN — no guard against rapid clicks
let isRecording = false;
chrome.action.onClicked.addListener(async (tab) => {
  if (isRecording) { stopRecording(); isRecording = false; }
  else { isRecording = true; startRecording(tab); } // Second click = "active stream" error
});

// ✅ CORRECT — use transitional states to lock out concurrent operations
// State machine: 'idle' → 'starting' → 'recording' → 'stopping' → 'idle'
// Store state in chrome.storage.session (survives SW restart, cleared on browser close)
chrome.action.onClicked.addListener(async (tab) => {
  const { recordingState = 'idle' } = await chrome.storage.session.get('recordingState');

  if (recordingState === 'starting' || recordingState === 'stopping') return;

  if (recordingState === 'idle') {
    await chrome.storage.session.set({ recordingState: 'starting' });
    try {
      await startRecording(tab);
      await chrome.storage.session.set({ recordingState: 'recording' });
      await chrome.action.setBadgeText({ text: 'REC' });
      await chrome.action.setBadgeBackgroundColor({ color: '#FF0000' });
    } catch (err) {
      console.error('Failed to start recording:', err);
      await chrome.storage.session.set({ recordingState: 'idle' });
    }
  } else if (recordingState === 'recording') {
    await chrome.storage.session.set({ recordingState: 'stopping' });
    try { await stopRecording(); }
    finally {
      await chrome.storage.session.set({ recordingState: 'idle' });
      await chrome.action.setBadgeText({ text: '' });
    }
  }
});

此模式适用于任何管理独占资源的 chrome API: chrome.tabCapture、chrome.desktopCapture、chrome.offscreen.createDocument(同一时间仅允许一个无痕文档)。参见 references/extensions/media-capture.md。

17. chrome.desktopCapture 需要具有 URL 访问权限的目标标签页

从服务工作线程调用 chrome.desktopCapture.chooseDesktopMedia() 时,必须将当前活动标签页作为 targetTab 参数传入。标签页对象必须填充 url 字段,这需要 "tabs" 权限。

// ❌ BROKEN — called without targetTab from service worker
chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], (streamId) => { ... });
// Error: "A target tab is required when called from a service worker context."

// ❌ BROKEN — tab doesn't have url field (missing "tabs" permission)
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], tab, (streamId) => { ... });
// Error: "targetTab doesn't have URL field set."

// ✅ CORRECT — "tabs" permission in manifest + pass tab object
// manifest.json: { "permissions": ["tabs", "desktopCapture"] }
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], tab, (streamId) => {
  if (!streamId) return; // User cancelled
});

注意: 对于仅录制标签页的情况,优先使用 chrome.tabCapture.getMediaStreamId()。只有在用户需要选择要捕获的屏幕或窗口时,才使用 chrome.desktopCapture。参见 references/extensions/media-capture.md。

18. 用户脚本:四个不太明显的陷阱

chrome.userScripts 会在运行时执行用户提供的代码。请将其用于脚本管理器和 用户自动化,而非扩展内置脚本。

  • 未启用时,访问属性会导致 API 抛出异常。 Chrome 138+ 要求用户在扩展程序详情页面中开启“允许用户脚本”;Chrome < 138 则要求开启开发者模式。调用任何 chrome.userScripts.* 方法前,都要先调用 isUserScriptsAvailable();若返回 false,则显示错误界面。
  • 扩展更新时,已注册的脚本会被清除。 请将配置保存在 chrome.storage 中;在 runtime.onInstalled 中针对 "update" 原因重新注册脚本。
  • 消息传递需要明确选择启用。 请先调用 configureWorld({ messaging: true });监听 runtime.onUserScriptMessage,而不是 runtime.onMessage。
  • **ScriptSource 约束:每个 js 条目必须且只能包含 code 或 file 其中之一。id 约束:**不能以下划线 _ 开头。

参见 references/extensions/user-scripts.md。

19. chrome.windows 没有 .query() 方法——请使用 getAll、getLastFocused 或 getCurrent

与 chrome.tabs.query() 不同,chrome.windows API 没有 .query() 方法。

// ❌ BROKEN — chrome.windows.query does not exist
const windows = await chrome.windows.query({ focused: true });
// TypeError: chrome.windows.query is not a function

// ✅ CORRECT — use the right method for your need
const focused = await chrome.windows.getLastFocused({ populate: true });
const current = await chrome.windows.getCurrent({ populate: true });
const all     = await chrome.windows.getAll({ populate: true });

chrome.windows 方法:getAll、getLastFocused、getCurrent、get(windowId)、create、update、remove。参见 references/extensions/tab-management.md。

20. 在服务工作线程中调用 chrome.permissions.request() 时,消息监听器中不得在此调用前使用 await

来自 UI 上下文(侧边栏、弹出窗口)的用户手势会通过 chrome.runtime.sendMessage 传递到服务工作线程的 onMessage 监听器——但只在当前同步执行期间有效。如果监听器在调用 chrome.permissions.request() 前执行了 await(即使只是短暂等待),用户手势也会失效,该调用会抛出 "This function must be called during a user gesture"。请将此调用作为监听器中的第一项操作,前面不要有任何等待——参见 references/extensions/permissions.md。

始终使用 Manifest V3

绝不生成 Manifest V2 代码。

  • 使用 background.service_worker,而非 background.scripts
  • 使用 chrome.action,而非 chrome.browserAction
  • 使用 chrome.scripting.executeScript,而非 chrome.tabs.executeScript
  • host_permissions 与 permissions 分开配置
  • HTML 中不得包含内联脚本——请使用 <script src="file.js">
  • 不得使用内联事件处理程序——请使用 addEventListener

第 2 部分 — 发布到 Chrome Web Store

维护 CHROMEWEBSTORE.md——这是 Chrome 扩展项目中所有 Chrome Web Store 商品信息元数据、权限使用理由、隐私披露、版本历史和发布准备情况的唯一真实来源。

核心工作流

每次以会影响 Chrome 扩展商品页面的方式处理项目时,都要更新(或创建)项目根目录下的 CHROMEWEBSTORE.md。此文件会记录开发者需要在 Chrome Developer Dashboard 中填写的所有内容,这样他们就能从 一份文档中复制粘贴,而不必等到发布时才手忙脚乱。

何时创建 CHROMEWEBSTORE.md

出现以下任何情况时,立即创建:

  • 用户表示要发布扩展
  • 用户要求“为应用商店做好准备”或“准备发布”
  • 你正在构建一个显然最终会发布到应用商店的新扩展
  • 用户询问应用商店的商品信息要求

请使用 references/webstore/chromewebstore-template.md 中的模板作为起点。生成文件前请先阅读。

何时更新 CHROMEWEBSTORE.md

遇到以下情况时更新:

  • 面向用户的更改:更新“Last Updated”日期,更新描述中的功能列表,并在版本历史记录中添加条目
  • manifest.json 更改:如果 permissions、host_permissions 或 content_scripts 有所更改,请更新“权限说明”部分——每项权限都需要提供审核团队能理解的通俗理由
  • 发布新版本:添加一条版本历史记录,包含版本号、日期和摘要
  • 与隐私相关的更改:如果数据收集、存储或传输方式有所更改,请更新“隐私与数据使用”部分和隐私政策
  • 素材更改:如果图标或 UI 有所更改,请注明哪些截图需要更新
  • 审核未通过后的回复:如果用户报告 CWS 审核未通过,请在文件中记录修复内容,并在版本历史记录中添加说明
如何填写

对于每个部分,请从实际项目文件中提取信息:

  1. 阅读 manifest.json,提取名称、版本、描述、permissions、host_permissions
  2. 扫描代码库,查找数据收集相关内容(存储、fetch 调用、分析)
  3. 检查图标文件及其尺寸
  4. 查看扩展程序的 UI,了解描述中应介绍的功能

面向商店的文案应具体、诚实,并突出用户收益。Chrome 网上应用店审核团队会拒绝含糊的描述。“让生活更轻松”会被拒绝。“突出显示任意网页中的搜索结果,并让你将其保存到本地列表”则会通过。

切勿提及实现细节。 用户关心的是扩展程序能为他们做什么,而不是它是如何构建的。删除任何关于 API、库、框架或代码模式的描述:

❌ 实现细节(删除)✅ 用户收益(保留)
“使用 MutationObserver 检测页面变化”“浏览时自动检测新内容”
“使用自定义元素和 Shadow DOM 构建”“无缝运行,不会影响网页样式”
“借助服务工作线程进行后台处理”“在后台安静运行,不会拖慢浏览器”
“利用 chrome.storage.sync API”“你的设置会在所有设备间同步”
“使用 declarativeNetRequest 进行过滤”“拦截广告和跟踪器,且不会读取网页内容”
CHROMEWEBSTORE.md 各部分

生成文件前,请阅读 references/webstore/chromewebstore-template.md——其中定义了各部分的内容和填写方法。风险最高的部分是“权限说明”:请为每项权限和每项 host_permission 提供具体、通俗的理由。“扩展程序正常运行所必需”会被拒绝。有关生成隐私政策的指导,请阅读 references/webstore/privacy-policy.md。

发布前检查清单

提交前,请逐项检查 references/webstore/review-checklist.md。首次提交最常见的失败原因:

  • 每项 permission 和 host_permission 都必须有具体说明(不能写“正常运行所必需”)
  • 隐私政策 URL 必须可访问,并且与数据使用披露表单一致
  • 至少有 1 张分辨率为 1280×800 或 640×400 的截图
  • ZIP 中不得包含 .git/、node_modules/、.env、CHROMEWEBSTORE.md
商店商品信息文案指南

有关文案指南和常见的审核未通过原因,请参阅 references/webstore/store-listing.md。 关键规则:开头应介绍功能(“突出显示任意网页中的搜索词”),而不是表达感受(“重新享受搜索的乐趣”)。


参考文件

编写代码或内容前,请阅读相关文件,了解详细的 API 模式和发布指南:

主题参考文件
权限references/extensions/permissions.md
侧边栏references/extensions/side-panel.md
内容脚本和 DOMreferences/extensions/content-scripts.md
弹出窗口references/extensions/popup-ui.md
Service Worker 生命周期references/extensions/service-worker.md
代码执行和 CSPreferences/extensions/csp-sandbox.md
API 调用references/extensions/api-calling.md
声明式网络请求references/extensions/declarative-net-request.md
Chrome Prompt APIreferences/extensions/prompt-api.md
DevTools 面板references/extensions/devtools.md
身份验证references/extensions/auth-identity.md
上下文菜单references/extensions/context-menus.md
地址栏references/extensions/omnibox.md
存储references/extensions/storage.md
标签页和窗口管理references/extensions/tab-management.md
标签页/桌面捕获references/extensions/media-capture.md
用户脚本references/extensions/user-scripts.md
消息传递references/extensions/message-passing.md
图标references/extensions/icons.md
CHROMEWEBSTORE.md 模板references/webstore/chromewebstore-template.md
隐私政策指南references/webstore/privacy-policy.md
发布前审核清单references/webstore/review-checklist.md
商店列表提示和拒绝原因references/webstore/store-listing.md

输出检查清单

交付前请核实每一项:

  • manifest_version: 3——任何地方都没有 V2 API
  • 清单中引用的所有图标文件都是真实文件且尺寸正确,或者省略图标
  • 侧边栏有明确的打开触发方式(不能只有清单声明)
  • 代码执行使用 sandbox/blob/srcdoc——扩展页面中不得使用 eval()
  • 如果访问 tab.url 或 tab.title,则声明 tabs 权限
  • 所有代码都使用 async/await——不使用 .then() 链
  • 内容脚本使用 requestAnimationFrame 批量更新 DOM
  • 服务工作线程不在全局变量中存储任何状态——使用 chrome.storage
  • HTML 中没有内联脚本或事件处理程序
  • 上下文菜单操作会向用户显示确认信息
  • 如果使用 chrome.action.* API,清单中包含 "action": {}(或更多内容)
  • 如果从侧边栏读取标签页或对其执行脚本:使用 tabs + host_permissions(而非 activeTab)
  • chrome.devtools.panels.create() 中的 DevTools 面板路径相对于扩展根目录
  • 离屏文档仅使用 chrome.runtime 消息传递——不得使用 chrome.downloads、chrome.action 等
  • chrome.notifications、chrome.action.setIcon 等中的所有图像引用都指向真实文件(或使用数据 URL)
  • 标签页/桌面捕获使用状态锁定,以防止重复启动错误
  • chrome.desktopCapture.chooseDesktopMedia 在具有 tabs 权限时传入 targetTab
  • chrome.windows 调用使用 getAll/getLastFocused/getCurrent——不得使用 .query()(该方法不存在)
  • 在服务工作线程的 onMessage 监听器中调用 chrome.permissions.request() 时,调用前不得使用 await(首次异步间隙后用户手势会失效)
  • 使用前检查 chrome.userScripts 是否可用(如果用户尚未启用该 API,调用时会抛出错误)
  • 用户脚本配置保存在 chrome.storage 中,并在 runtime.onInstalled 的 "update" 原因触发时恢复
  • 用户脚本发送消息前调用 configureWorld({ messaging: true });监听 onUserScriptMessage,而非 onMessage
  • 每个 ScriptSource 条目必须且只能包含 code 或 file 其中之一(不能同时包含,也不能都不包含)
  • 用户脚本的 id 值不能以下划线开头
  • sidePanel.setPanelBehavior 使用 openPanelOnActionClick——不得使用 openPanelOnActionIconClick
  • 对所有异步操作进行错误处理
  • host_permissions 限定到特定域名(除非确有需要,否则不要使用 <all_urls>)
  • 对于包含异步响应的 onMessage 监听器,使用 return true
  • 在 chrome.contextMenus 的 contexts 中使用 "tab" 时,需要 Chrome M150+

评论

支持 Markdown