浏览器插件 chrome.* API 全量速查大全
本手册按命名空间组织。每个命名空间一个 ## 主节,每个常用方法一个 ### 子节,配参数表与最小可运行示例。代码若运行在 Service Worker(背景脚本,下文简称 SW)、内容脚本(content script)、弹窗页(popup)或选项页(options),均在示例中标注上下文。 下表用于快速判断某 API 需要在 manifest.json 声明哪些权限,以及可在哪些上下文调用。「SW」=Service Worker;「CS」=内容脚本;「页面」=popup/options/sidePanel 等扩展页。 全上下文可用,无需任何权限,是消
官方文档:https://developer.chrome.com/docs/extensions/reference/api
适用版本:Manifest V3 / Chrome 138+
核实日期:2026-06-06
本篇为系列「API 参考篇」。所有chrome.*命名空间方法在 MV3 下默认返回 Promise(同时仍兼容回调风格),本文示例一律采用 Promise 风格。
本手册按命名空间组织。每个命名空间一个 ## 主节,每个常用方法一个 ### 子节,配参数表与最小可运行示例。代码若运行在 Service Worker(背景脚本,下文简称 SW)、内容脚本(content script)、弹窗页(popup)或选项页(options),均在示例中标注上下文。
API 总览:权限与可用上下文
下表用于快速判断某 API 需要在 manifest.json 声明哪些权限,以及可在哪些上下文调用。「SW」=Service Worker;「CS」=内容脚本;「页面」=popup/options/sidePanel 等扩展页。
| API 命名空间 | 所需 permission | 可用上下文 | 备注 |
|---|---|---|---|
chrome.runtime |
无 | SW / CS / 页面 | 全上下文可用,无需声明 |
chrome.tabs |
无(读 url/title 需 tabs 或 host 权限) |
SW / 页面 | CS 不可直接用 |
chrome.windows |
无 | SW / 页面 | 同 tabs |
chrome.storage |
storage |
SW / CS / 页面 | 四区共享 API |
chrome.scripting |
scripting + host 权限 |
SW / 页面 | 注入目标需 host 权限 |
chrome.action |
无(manifest 需 action 键) |
SW / 页面 | 工具栏图标 |
chrome.contextMenus |
contextMenus |
SW | 右键菜单 |
chrome.alarms |
alarms |
SW | 定时任务 |
chrome.commands |
无(manifest 需 commands 键) |
SW | 快捷键 |
chrome.notifications |
notifications |
SW / 页面 | 系统通知 |
chrome.sidePanel |
sidePanel |
SW / 页面 | 侧边栏,Chrome 114+ |
chrome.permissions |
无 | SW / 页面 | 运行时申请 optional 权限 |
chrome.cookies |
cookies + host 权限 |
SW / 页面 | 读写 Cookie |
chrome.webNavigation |
webNavigation |
SW | 导航事件监听 |
chrome.declarativeNetRequest |
declarativeNetRequest 或 declarativeNetRequestWithHostAccess |
SW | 声明式拦截 |
chrome.identity |
identity |
SW / 页面 | OAuth |
chrome.offscreen |
offscreen |
SW | 在 SW 中用 DOM |
chrome.i18n |
无(需 _locales/) |
SW / CS / 页面 | 国际化 |
chrome.downloads |
downloads |
SW / 页面 | 下载管理 |
chrome.bookmarks |
bookmarks |
SW / 页面 | 书签 |
chrome.history |
history |
SW / 页面 | 历史记录 |
chrome.runtime
全上下文可用,无需任何权限,是消息通信与生命周期管理的核心。
chrome.runtime.id
只读属性,返回当前扩展的 ID(32 位小写字母字符串)。可在 CS 中用于拼接 chrome-extension:// URL。
// 上下文:任意
console.log(chrome.runtime.id); // "abcdefghijklmnopabcdefghijklmnop"
chrome.runtime.getURL(path)
把扩展内的相对路径转换为完整的 chrome-extension://<id>/... 绝对 URL。常用于在 CS 中加载 web_accessible_resources 里声明的资源。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path |
string |
必填 | 相对扩展根目录的路径,前导 / 可选 |
// 上下文:内容脚本
const imgUrl = chrome.runtime.getURL("assets/logo.png");
const img = document.createElement("img");
img.src = imgUrl;
document.body.append(img);
// 错误:直接拼字符串,扩展 ID 改变后失效
// const bad = "chrome-extension://abcd.../assets/logo.png"; // 错误:硬编码 ID
chrome.runtime.getManifest()
同步返回解析后的 manifest.json 对象,无参数。用于读取版本号等。
// 上下文:任意
const { version, name } = chrome.runtime.getManifest();
console.log(`${name} v${version}`);
chrome.runtime.sendMessage(extensionId?, message, options?)
向扩展内其它上下文(SW、其它页面)发送一次性消息,返回 Promise 解析为接收方的响应。单条消息上限 64 MiB。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
extensionId |
string |
当前扩展 | 目标扩展 ID,发给自己可省略 |
message |
any |
必填 | 任意 JSON 可序列化值 |
options |
object |
undefined |
includeTlsChannelId 等,少用 |
// 上下文:popup
const res = await chrome.runtime.sendMessage({ type: "getCount" });
console.log(res.count);
// 错误:消息含函数/DOM 节点,无法 JSON 序列化
// await chrome.runtime.sendMessage({ el: document.body }); // 错误:不可序列化
chrome.runtime.onMessage
监听 sendMessage 发来的消息。回调签名 (message, sender, sendResponse)。异步回复必须 return true 以保持消息通道开放;或在 Chrome 148+ 直接返回 Promise。
| 回调参数 | 类型 | 说明 |
|---|---|---|
message |
any |
发送方传入的消息体 |
sender |
MessageSender |
含 tab、id、url、origin 等 |
sendResponse |
function |
调用以回送响应,可异步调用 |
// 上下文:Service Worker
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === "getCount") {
chrome.storage.local.get("count").then(({ count }) => {
sendResponse({ count: count ?? 0 });
});
return true; // 关键:异步回复需返回 true 保持通道
}
// 错误:async 监听器整体返回 Promise 在 148 前不被识别,回复丢失
});
chrome.runtime.connect(extensionId?, connectInfo?)
建立长连接,返回 Port 对象,适合高频或流式通信。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
extensionId |
string |
当前扩展 | 目标扩展 ID |
connectInfo |
object |
undefined |
{ name } 命名通道,便于区分连接类型 |
// 上下文:popup
const port = chrome.runtime.connect({ name: "live-feed" });
port.postMessage({ subscribe: true });
port.onMessage.addListener((m) => console.log("推送:", m));
port.onDisconnect.addListener(() => console.log("通道关闭"));
chrome.runtime.onConnect
监听 connect 建立的连接,回调收到 Port。
// 上下文:Service Worker
chrome.runtime.onConnect.addListener((port) => {
if (port.name !== "live-feed") return;
port.onMessage.addListener((m) => {
if (m.subscribe) port.postMessage({ tick: Date.now() });
});
});
chrome.runtime.onInstalled
扩展首次安装、更新或 Chrome 升级时触发。回调参数 details.reason 取值:"install"(首装)、"update"(更新)、"chrome_update"(浏览器升级)、"shared_module_update"(依赖模块更新)。
// 上下文:Service Worker
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === "install") {
chrome.storage.local.set({ count: 0 }); // 初始化默认配置
} else if (details.reason === "update") {
console.log("从", details.previousVersion, "升级");
}
});
chrome.runtime.onStartup
用户启动 Chrome、加载该扩展的 profile 时触发一次。无参数。注意:扩展更新或安装时不会触发。
// 上下文:Service Worker
chrome.runtime.onStartup.addListener(() => {
console.log("浏览器启动,SW 唤醒");
});
chrome.runtime.lastError
只读属性,仅在回调风格 API 出错时于回调内有值。Promise 风格用 try/catch 即可,一般不再需要它。
// 上下文:Service Worker(回调风格 API 仍存在的场景)
chrome.tabs.sendMessage(tabId, { ping: 1 }, () => {
if (chrome.runtime.lastError) {
console.warn("目标无监听器:", chrome.runtime.lastError.message);
}
});
chrome.runtime.getContexts(filter)
Chrome 116+。查询当前扩展存活的所有上下文(SW、offscreen、popup、tab 等),返回 ExtensionContext[]。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
filter |
object |
必填 | 含 contextTypes、documentUrls、tabIds 等过滤字段 |
contextTypes 取值:"TAB"、"POPUP"、"BACKGROUND"、"OFFSCREEN_DOCUMENT"、"SIDE_PANEL"、"DEVELOPER_TOOLS"。
// 上下文:Service Worker
const ctxs = await chrome.runtime.getContexts({
contextTypes: ["OFFSCREEN_DOCUMENT"]
});
const hasOffscreen = ctxs.length > 0; // 判断 offscreen 是否已存在
chrome.tabs
无需权限即可调用,但读取 url、title、favIconUrl、pendingUrl 需要 tabs 权限或匹配该标签的 host 权限。不可在 CS 中直接使用。
chrome.tabs.query(queryInfo)
按条件查询标签页,返回 Tab[]。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
queryInfo.active |
boolean |
不限 | 是否为窗口内激活标签 |
queryInfo.currentWindow |
boolean |
不限 | 是否在当前窗口 |
queryInfo.url |
string | string[] |
不限 | match pattern 过滤,需 host 权限 |
queryInfo.status |
string |
不限 | "loading" 或 "complete" |
queryInfo.pinned |
boolean |
不限 | 是否固定 |
// 上下文:Service Worker
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
console.log("当前标签:", tab.id, tab.url);
// 错误:不传任何条件会返回所有窗口所有标签,浪费
// const all = await chrome.tabs.query({}); // 错误:全表扫描
chrome.tabs.get(tabId)
按 ID 获取单个标签的最新信息。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
tabId |
number |
必填 | 标签 ID |
// 上下文:Service Worker
const tab = await chrome.tabs.get(123);
chrome.tabs.create(createProperties)
新建标签页,返回新建的 Tab。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
string |
新标签页 | 目标 URL,扩展页用 chrome.runtime.getURL |
active |
boolean |
true |
是否前台激活 |
index |
number |
末尾 | 在窗口中的位置 |
windowId |
number |
当前窗口 | 目标窗口 ID |
pinned |
boolean |
false |
是否固定 |
// 上下文:Service Worker
await chrome.tabs.create({ url: chrome.runtime.getURL("options.html") });
chrome.tabs.update(tabId?, updateProperties)
修改标签属性(导航、激活、静音等)。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
tabId |
number |
当前激活标签 | 省略则作用于激活标签 |
updateProperties.url |
string |
不变 | 导航到新 URL |
updateProperties.active |
boolean |
不变 | 激活该标签 |
updateProperties.muted |
boolean |
不变 | 静音/取消静音 |
updateProperties.pinned |
boolean |
不变 | 固定/取消固定 |
// 上下文:Service Worker
await chrome.tabs.update(tabId, { url: "https://example.com", active: true });
chrome.tabs.remove(tabIds)
关闭一个或多个标签。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
tabIds |
number | number[] |
必填 | 单个或数组 |
// 上下文:Service Worker
await chrome.tabs.remove([101, 102]); // 批量关闭
chrome.tabs.sendMessage(tabId, message, options?)
向指定标签内的内容脚本发送消息,返回 Promise 解析为 CS 的响应。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
tabId |
number |
必填 | 目标标签 |
message |
any |
必填 | JSON 可序列化值 |
options.frameId |
number |
所有 frame | 仅发给指定子框架 |
// 上下文:Service Worker
try {
const res = await chrome.tabs.sendMessage(tabId, { type: "highlight" });
} catch (e) {
// 该标签无内容脚本监听时抛错,需捕获
console.warn("无 CS 接收:", e.message);
}
chrome.tabs.captureVisibleTab(windowId?, options?)
截取指定窗口当前可见标签的画面,返回 data URL。需要 activeTab 或 <all_urls> host 权限。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
windowId |
number |
当前窗口 | 目标窗口 ID |
options.format |
string |
"jpeg" |
"jpeg" 或 "png" |
options.quality |
number |
由格式定 | 0-100,仅 jpeg 有效 |
// 上下文:Service Worker(需 activeTab 权限)
const dataUrl = await chrome.tabs.captureVisibleTab(undefined, {
format: "png"
});
chrome.tabs.group(options)
将标签加入标签组,返回 groupId。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options.tabIds |
number | number[] |
必填 | 要分组的标签 |
options.groupId |
number |
新建组 | 加入已有组的 ID |
options.createProperties.windowId |
number |
当前窗口 | 新组所在窗口 |
// 上下文:Service Worker
const groupId = await chrome.tabs.group({ tabIds: [101, 102] });
await chrome.tabGroups.update(groupId, { title: "工作", color: "blue" });
chrome.tabs.onUpdated
标签更新(URL、加载状态、标题等变化)时触发。回调 (tabId, changeInfo, tab)。
// 上下文:Service Worker
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === "complete" && tab.url?.startsWith("https://")) {
console.log("页面加载完成:", tab.url);
}
});
chrome.tabs.onActivated
用户切换激活标签时触发。回调 activeInfo 含 tabId、windowId。
// 上下文:Service Worker
chrome.tabs.onActivated.addListener(({ tabId }) => {
console.log("切换到标签", tabId);
});
chrome.tabs.onRemoved
标签关闭时触发。回调 (tabId, removeInfo),removeInfo.isWindowClosing 表示是否因窗口关闭。
// 上下文:Service Worker
chrome.tabs.onRemoved.addListener((tabId, info) => {
console.log("关闭标签", tabId, "窗口关闭:", info.isWindowClosing);
});
chrome.windows
操作浏览器窗口,无需权限。
chrome.windows.create(createData)
新建窗口,返回 Window。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
string | string[] |
新标签页 | 初始打开的 URL |
type |
string |
"normal" |
"normal" / "popup" / "panel" |
state |
string |
"normal" |
"normal" / "minimized" / "maximized" / "fullscreen" |
focused |
boolean |
true |
是否获取焦点 |
width / height |
number |
自动 | 像素尺寸 |
left / top |
number |
自动 | 屏幕位置 |
incognito |
boolean |
false |
是否隐身窗口 |
// 上下文:Service Worker
await chrome.windows.create({
url: chrome.runtime.getURL("panel.html"),
type: "popup",
width: 400,
height: 600
});
chrome.windows.get(windowId, queryOptions?)
获取指定窗口。queryOptions.populate 为 true 时附带 tabs 数组。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
windowId |
number |
必填 | 窗口 ID |
queryOptions.populate |
boolean |
false |
是否包含标签列表 |
// 上下文:Service Worker
const win = await chrome.windows.get(winId, { populate: true });
console.log("窗口含标签数:", win.tabs.length);
chrome.windows.getAll(queryOptions?)
返回所有窗口数组。
// 上下文:Service Worker
const wins = await chrome.windows.getAll({ populate: false });
chrome.windows.update(windowId, updateInfo)
修改窗口状态、位置、尺寸。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
windowId |
number |
必填 | 窗口 ID |
updateInfo.focused |
boolean |
不变 | 置顶聚焦 |
updateInfo.state |
string |
不变 | 同 create 的 state |
updateInfo.drawAttention |
boolean |
不变 | 任务栏闪烁提醒 |
// 上下文:Service Worker
await chrome.windows.update(winId, { focused: true, state: "maximized" });
chrome.windows.remove(windowId)
关闭指定窗口。
// 上下文:Service Worker
await chrome.windows.remove(winId);
chrome.storage
需 storage 权限。四个存储区共享同一套方法 API。所有值必须 JSON 可序列化。
四区配额对比
| 区 | 总配额 | 单项上限 | 最大项数 | 写频率限制 | 同步 | 上下文 |
|---|---|---|---|---|---|---|
local |
10 MB | 无 | 无 | 无 | 否 | SW/CS/页面 |
sync |
100 KB(102,400 字节) | 8 KB(8,192 字节) | 512 | 120 次/分、1,800 次/小时 | 跨设备 | SW/CS/页面 |
session |
10 MB | 无 | 无 | 无 | 否(内存,重启清空) | 默认仅 SW,可放开 |
managed |
— | — | — | 只读 | 由策略下发 | SW/CS/页面 |
local 在 Chrome 113 及更早为 5 MB;session 在 Chrome 111 及更早为 1 MB。managed 由企业策略 JSON 提供,扩展只读。
get(keys?)
读取一个或多个键,返回对象。keys 为 null 或省略时读取整个区。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
keys |
string | string[] | object | null |
null |
传对象时其值作为缺省返回值 |
// 上下文:任意
const { theme = "light" } = await chrome.storage.local.get({ theme: "light" });
const multi = await chrome.storage.sync.get(["a", "b"]);
set(items)
写入键值对,整体覆盖同名键。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
items |
object |
必填 | 一次写多个键,减少写次数 |
// 上下文:任意
await chrome.storage.local.set({ count: 5, theme: "dark" });
// 错误:循环里逐条 set,撞 sync 写频率上限
// for (const k in data) await chrome.storage.sync.set({ [k]: data[k] }); // 错误:高频写
remove(keys)
删除指定键。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
keys |
string | string[] |
必填 | 要删除的键 |
// 上下文:任意
await chrome.storage.local.remove(["count", "theme"]);
clear()
清空整个存储区,无参数。
// 上下文:任意
await chrome.storage.session.clear();
getBytesInUse(keys?)
返回指定键(或整区)占用的字节数。sync 区按此判断是否接近配额。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
keys |
string | string[] | null |
null |
省略统计整区 |
// 上下文:任意
const used = await chrome.storage.sync.getBytesInUse(null);
console.log(`已用 ${used}/102400 字节`);
setAccessLevel(accessOptions)
控制存储区能否被不可信上下文(如 MAIN world 注入脚本)访问。主要用于 session 区。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
accessOptions.accessLevel |
string |
"TRUSTED_CONTEXTS" |
访问级别枚举 |
accessLevel 取值:"TRUSTED_CONTEXTS"(仅扩展自身上下文,默认)、"TRUSTED_AND_UNTRUSTED_CONTEXTS"(含内容脚本等外部上下文)。
// 上下文:Service Worker
// 允许内容脚本读写 session 区
await chrome.storage.session.setAccessLevel({
accessLevel: "TRUSTED_AND_UNTRUSTED_CONTEXTS"
});
onChanged
任意键变化时触发。storage.onChanged 监听所有区;各区也有自身 onChanged。回调 (changes, areaName),changes 为 { key: { oldValue, newValue } }。
// 上下文:任意
chrome.storage.onChanged.addListener((changes, area) => {
if (area === "local" && changes.theme) {
console.log("主题从", changes.theme.oldValue, "变为", changes.theme.newValue);
}
});
chrome.scripting
需 scripting 权限,注入目标还需对应 host 权限(或 activeTab)。仅可在 SW 与扩展页调用。
executeScript(injection)
向目标注入 JS 函数或文件,返回 InjectionResult[]。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
injection.target |
object |
必填 | { tabId, frameIds?, allFrames? } |
injection.func |
function |
— | 要执行的函数(与 files 二选一) |
injection.args |
any[] |
[] |
传给 func 的参数,须可序列化 |
injection.files |
string[] |
— | 要注入的脚本文件路径 |
injection.world |
string |
"ISOLATED" |
执行环境,见下 |
injection.injectImmediately |
boolean |
false |
尽早注入而不等导航稳定 |
world 取值:"ISOLATED"(扩展专属隔离环境,与页面共享 DOM 但 JS 变量隔离,默认)、"MAIN"(页面主世界,与网页脚本共享 window,可访问页面变量但无 chrome.* 高权 API)。
// 上下文:Service Worker
const [result] = await chrome.scripting.executeScript({
target: { tabId },
func: (prefix) => prefix + document.title, // 函数体在目标页运行
args: ["标题:"]
});
console.log(result.result);
// MAIN world 读取页面全局变量
await chrome.scripting.executeScript({
target: { tabId },
world: "MAIN",
func: () => window.__APP_STATE__
});
// 错误:func 引用了 SW 作用域的外部变量(闭包不跨进程)
// const x = 1; executeScript({ target, func: () => x }); // 错误:func 内 x 未定义
insertCSS(injection)
注入样式表,返回 Promise<void>。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
injection.target |
object |
必填 | { tabId, frameIds?, allFrames? } |
injection.css |
string |
— | CSS 字符串(与 files 二选一) |
injection.files |
string[] |
— | CSS 文件路径 |
injection.origin |
string |
"AUTHOR" |
"AUTHOR" 或 "USER"(优先级不同) |
// 上下文:Service Worker
await chrome.scripting.insertCSS({
target: { tabId },
css: "body { filter: invert(1); }"
});
removeCSS(injection)
移除此前用 insertCSS 注入的样式,参数须与注入时一致。
// 上下文:Service Worker
await chrome.scripting.removeCSS({
target: { tabId },
css: "body { filter: invert(1); }" // 须与 insert 时完全相同
});
registerContentScripts(scripts)
动态注册内容脚本(无需写在 manifest),返回 Promise<void>。
| 参数(每项) | 类型 | 默认值 | 说明 |
|---|---|---|---|
id |
string |
必填 | 唯一标识,不能以 _ 开头 |
matches |
string[] |
必填 | match pattern |
js / css |
string[] |
— | 脚本/样式文件 |
runAt |
string |
"document_idle" |
"document_start" / "document_end" / "document_idle" |
world |
string |
"ISOLATED" |
同 executeScript |
persistAcrossSessions |
boolean |
true |
浏览器重启后是否保留 |
allFrames |
boolean |
false |
是否注入所有子框架 |
// 上下文:Service Worker
await chrome.scripting.registerContentScripts([{
id: "auto-cs",
matches: ["https://example.com/*"],
js: ["injected.js"],
runAt: "document_idle"
}]);
updateContentScripts(scripts)
按 id 更新已注册脚本的字段,返回 Promise<void>。
// 上下文:Service Worker
await chrome.scripting.updateContentScripts([{
id: "auto-cs",
matches: ["https://example.com/*", "https://example.org/*"]
}]);
unregisterContentScripts(filter?)
注销动态脚本。省略 filter 注销全部。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
filter.ids |
string[] |
全部 | 要注销的脚本 ID |
// 上下文:Service Worker
await chrome.scripting.unregisterContentScripts({ ids: ["auto-cs"] });
getRegisteredContentScripts(filter?)
返回已注册的动态脚本列表。
// 上下文:Service Worker
const scripts = await chrome.scripting.getRegisteredContentScripts();
console.log("已注册数量:", scripts.length);
chrome.action
manifest 需声明 action 键。无需 permission。控制工具栏图标。
setBadgeText(details)
设置图标角标文字(最多约 4 字符可见)。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.text |
string |
"" |
角标文本,空串清除 |
details.tabId |
number |
全局 | 仅对指定标签生效 |
// 上下文:Service Worker
await chrome.action.setBadgeText({ text: "5", tabId });
setBadgeBackgroundColor(details)
设置角标背景色。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.color |
string | number[] |
— | "#FF0000" 或 [255,0,0,255] |
details.tabId |
number |
全局 | 限定标签 |
// 上下文:Service Worker
await chrome.action.setBadgeBackgroundColor({ color: "#D32F2F" });
setIcon(details)
动态切换图标,返回 Promise<void>。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.path |
string | object |
— | { "16": "...", "32": "..." } 多尺寸 |
details.imageData |
ImageData | object |
— | 由 OffscreenCanvas 生成的位图 |
details.tabId |
number |
全局 | 限定标签 |
// 上下文:Service Worker
await chrome.action.setIcon({ path: { 16: "on16.png", 32: "on32.png" } });
setPopup(details)
设置点击图标时弹出的 HTML。设为 "" 则恢复触发 onClicked。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.popup |
string |
— | popup 页路径,空串禁用 popup |
details.tabId |
number |
全局 | 限定标签 |
// 上下文:Service Worker
await chrome.action.setPopup({ popup: "popup.html" });
setTitle(details)
设置图标悬停提示。
// 上下文:Service Worker
await chrome.action.setTitle({ title: "未读 5 条" });
enable(tabId?) / disable(tabId?)
启用/禁用图标点击。禁用后图标变灰。
// 上下文:Service Worker
await chrome.action.disable(tabId);
await chrome.action.enable(tabId);
onClicked
仅当未设置 popup 时,点击图标触发。回调收到当前 Tab。
// 上下文:Service Worker
chrome.action.onClicked.addListener((tab) => {
console.log("点击图标,当前页:", tab.url);
});
chrome.contextMenus
需 contextMenus 权限。仅在 SW 中创建。
create(createProperties, callback?)
创建右键菜单项,同步返回菜单 ID。应在 onInstalled 中创建,避免重复。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
id |
string |
自动 | 菜单项唯一 ID |
title |
string |
必填(除分隔符外) | 显示文字,%s 表示选中文本 |
contexts |
string[] |
["page"] |
"page"/"selection"/"link"/"image"/"editable"/"action" 等 |
type |
string |
"normal" |
"normal"/"checkbox"/"radio"/"separator" |
parentId |
string |
无 | 父菜单 ID,构建子菜单 |
documentUrlPatterns |
string[] |
不限 | 仅在匹配页面显示 |
// 上下文:Service Worker
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "search-sel",
title: '搜索 "%s"',
contexts: ["selection"]
});
});
update(id, updateProperties)
修改已有菜单项。
// 上下文:Service Worker
await chrome.contextMenus.update("search-sel", { title: "已禁用", enabled: false });
remove(menuItemId) / removeAll()
删除单个或全部菜单项。
// 上下文:Service Worker
await chrome.contextMenus.remove("search-sel");
await chrome.contextMenus.removeAll();
onClicked
菜单项被点击时触发。回调 (info, tab),info 含 menuItemId、selectionText、linkUrl、checked 等。
// 上下文:Service Worker
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "search-sel") {
chrome.tabs.create({
url: "https://www.google.com/search?q=" + encodeURIComponent(info.selectionText)
});
}
});
chrome.alarms
需 alarms 权限。MV3 中替代 setTimeout/setInterval(SW 随时可能休眠)。
最小周期:Chrome 120 起从 1 分钟降为 30 秒(periodInMinutes: 0.5)。已打包扩展中小于 0.5 的值不被采纳并产生警告;未打包扩展无此限制。Chrome 117 起最多 500 个活动 alarm。
create(name?, alarmInfo)
创建定时器,返回 Promise<void>。同名 alarm 会被覆盖。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name |
string |
"" |
alarm 名称 |
alarmInfo.when |
number |
— | 绝对触发时间(epoch ms),与 delay 二选一 |
alarmInfo.delayInMinutes |
number |
— | 首次触发延迟(分钟) |
alarmInfo.periodInMinutes |
number |
— | 重复周期(分钟),最小 0.5 |
// 上下文:Service Worker
await chrome.alarms.create("sync", {
delayInMinutes: 1,
periodInMinutes: 30
});
// 错误:MV3 的 SW 会休眠,setInterval 不可靠
// setInterval(sync, 1800000); // 错误:SW 休眠后定时丢失
get(name?)
获取指定 alarm,返回 Alarm | undefined。
// 上下文:Service Worker
const a = await chrome.alarms.get("sync");
console.log(a?.scheduledTime);
getAll()
返回所有 alarm 数组。
// 上下文:Service Worker
const all = await chrome.alarms.getAll();
clear(name?) / clearAll()
清除指定或全部 alarm,返回 Promise<boolean>。
// 上下文:Service Worker
await chrome.alarms.clear("sync");
await chrome.alarms.clearAll();
onAlarm
alarm 到期触发,回调收到 Alarm 对象(含 name、scheduledTime、periodInMinutes)。
// 上下文:Service Worker
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "sync") doSync();
});
chrome.commands
manifest 需声明 commands 键。无需 permission。绑定键盘快捷键。
getAll()
返回已注册命令及其当前快捷键。
// 上下文:Service Worker
const cmds = await chrome.commands.getAll();
cmds.forEach((c) => console.log(c.name, c.shortcut));
onCommand
自定义命令触发时回调 (command, tab)。保留命令 _execute_action(打开 popup)与 _execute_browser_action 由浏览器直接处理,不触发 onCommand。
// 上下文:Service Worker
chrome.commands.onCommand.addListener((command, tab) => {
if (command === "toggle-feature") {
chrome.tabs.sendMessage(tab.id, { type: "toggle" });
}
});
manifest 配置示例:
{
"commands": {
"toggle-feature": {
"suggested_key": { "default": "Ctrl+Shift+Y" },
"description": "切换功能"
},
"_execute_action": {
"suggested_key": { "default": "Ctrl+Shift+U" }
}
}
}
chrome.notifications
需 notifications 权限。
create(notificationId?, options)
创建系统通知,返回最终 notificationId。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
notificationId |
string |
自动生成 | 省略则随机生成 |
options.type |
string |
必填 | "basic"/"image"/"list"/"progress" |
options.iconUrl |
string |
必填 | 图标 URL(扩展内资源) |
options.title |
string |
必填 | 标题 |
options.message |
string |
必填 | 正文 |
options.buttons |
object[] |
无 | 最多 2 个,{ title } |
options.priority |
number |
0 |
-2 到 2 |
// 上下文:Service Worker
await chrome.notifications.create("done", {
type: "basic",
iconUrl: chrome.runtime.getURL("icon128.png"),
title: "任务完成",
message: "已同步 5 项",
buttons: [{ title: "查看" }]
});
update(notificationId, options)
更新已有通知,返回 Promise<boolean>(是否存在)。
// 上下文:Service Worker
await chrome.notifications.update("done", { message: "已同步 6 项" });
clear(notificationId)
清除通知,返回 Promise<boolean>。
// 上下文:Service Worker
await chrome.notifications.clear("done");
onClicked / onButtonClicked
通知本体或按钮被点击时触发。
// 上下文:Service Worker
chrome.notifications.onClicked.addListener((id) => {
console.log("点击通知", id);
});
chrome.notifications.onButtonClicked.addListener((id, btnIndex) => {
if (id === "done" && btnIndex === 0) chrome.tabs.create({ url: "result.html" });
});
chrome.sidePanel
需 sidePanel 权限,Chrome 114+。manifest 可配 side_panel.default_path。
setPanelBehavior(behavior)
控制点击工具栏图标是否打开侧边栏。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
behavior.openPanelOnActionClick |
boolean |
false |
点图标即开侧边栏 |
// 上下文:Service Worker
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
setOptions(options)
为全局或指定标签设置侧边栏页面与启用状态。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options.tabId |
number |
全局 | 限定标签 |
options.path |
string |
— | 侧边栏 HTML 路径 |
options.enabled |
boolean |
true |
是否在该标签启用 |
// 上下文:Service Worker
await chrome.sidePanel.setOptions({
tabId,
path: "panel.html",
enabled: true
});
open(options)
以编程方式打开侧边栏。必须在用户手势回调内调用(如 onClicked、onCommand)。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options.tabId |
number |
— | 在该标签打开(与 windowId 二选一) |
options.windowId |
number |
— | 在该窗口打开 |
// 上下文:Service Worker(用户手势内)
chrome.action.onClicked.addListener(async (tab) => {
await chrome.sidePanel.open({ tabId: tab.id });
});
// 错误:在非手势上下文(如 alarm)调用会被拒绝
// chrome.alarms.onAlarm.addListener(() => chrome.sidePanel.open({...})); // 错误:无手势
getOptions(options)
查询当前侧边栏配置。
// 上下文:Service Worker
const opts = await chrome.sidePanel.getOptions({ tabId });
chrome.permissions
无需 permission。运行时申请 manifest optional_permissions / optional_host_permissions 中声明的可选权限。
contains(permissions)
检查是否已拥有某权限,返回 Promise<boolean>。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
permissions.permissions |
string[] |
[] |
API 权限名 |
permissions.origins |
string[] |
[] |
host 权限 match pattern |
// 上下文:页面或 Service Worker
const has = await chrome.permissions.contains({ permissions: ["downloads"] });
request(permissions)
弹窗请求权限,返回 Promise<boolean>。必须在用户手势内调用。
// 上下文:popup(按钮点击回调内)
button.addEventListener("click", async () => {
const granted = await chrome.permissions.request({
permissions: ["downloads"],
origins: ["https://*.example.com/*"]
});
if (granted) startDownload();
});
// 错误:页面加载时直接请求,无手势会失败
// window.onload = () => chrome.permissions.request({...}); // 错误:无用户手势
remove(permissions)
撤销已授予的可选权限,返回 Promise<boolean>。
// 上下文:页面或 Service Worker
await chrome.permissions.remove({ permissions: ["downloads"] });
getAll()
返回当前所有有效权限(含 manifest 静态权限与已授予的可选权限)。
// 上下文:页面或 Service Worker
const all = await chrome.permissions.getAll();
console.log(all.permissions, all.origins);
chrome.cookies
需 cookies 权限 + 目标域的 host 权限。
get(details)
读取单个 Cookie,返回 Cookie | null。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.url |
string |
必填 | Cookie 所属 URL |
details.name |
string |
必填 | Cookie 名 |
details.storeId |
string |
当前 | Cookie store ID |
// 上下文:Service Worker(需 host 权限)
const c = await chrome.cookies.get({ url: "https://example.com", name: "sid" });
console.log(c?.value);
getAll(details)
按条件批量获取 Cookie,返回 Cookie[]。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.domain |
string |
不限 | 域过滤 |
details.name |
string |
不限 | 名称过滤 |
details.url |
string |
不限 | URL 过滤 |
details.secure |
boolean |
不限 | 仅安全 Cookie |
// 上下文:Service Worker
const cookies = await chrome.cookies.getAll({ domain: "example.com" });
set(details)
写入或更新 Cookie,返回写入的 Cookie。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.url |
string |
必填 | 关联 URL |
details.name |
string |
"" |
Cookie 名 |
details.value |
string |
"" |
Cookie 值 |
details.expirationDate |
number |
会话级 | epoch 秒,省略为会话 Cookie |
details.httpOnly |
boolean |
false |
是否 HttpOnly |
details.sameSite |
string |
"unspecified" |
"no_restriction"/"lax"/"strict" |
// 上下文:Service Worker
await chrome.cookies.set({
url: "https://example.com",
name: "theme",
value: "dark",
expirationDate: Math.floor(Date.now() / 1000) + 86400
});
remove(details)
删除 Cookie。
// 上下文:Service Worker
await chrome.cookies.remove({ url: "https://example.com", name: "theme" });
onChanged
Cookie 增删改时触发。回调 changeInfo 含 cookie、removed、cause。
// 上下文:Service Worker
chrome.cookies.onChanged.addListener((info) => {
console.log(info.cause, info.removed, info.cookie.name);
});
chrome.webNavigation
需 webNavigation 权限。监听页面导航各阶段。所有事件支持 addListener(cb, filter?),filter.url 可按 URL 条件过滤。回调含 tabId、frameId、url、timeStamp。
onCommitted
导航已确定并开始加载文档时触发(此时 URL 与导航类型已定)。
// 上下文:Service Worker
chrome.webNavigation.onCommitted.addListener((d) => {
if (d.frameId === 0) console.log("主框架导航至", d.url, d.transitionType);
}, { url: [{ hostSuffix: "example.com" }] });
onCompleted
文档及子资源加载完成时触发。
// 上下文:Service Worker
chrome.webNavigation.onCompleted.addListener((d) => {
if (d.frameId === 0) console.log("加载完成", d.url);
});
onHistoryStateUpdated
SPA 通过 history.pushState/replaceState 改变 URL 而无整页刷新时触发,是监听单页应用路由变化的关键。
// 上下文:Service Worker
chrome.webNavigation.onHistoryStateUpdated.addListener((d) => {
console.log("SPA 路由变化", d.url);
});
其它常用事件:onBeforeNavigate(导航即将发生)、onDOMContentLoaded(DOM 就绪)、onErrorOccurred(导航失败)、onReferenceFragmentUpdated(仅 # 锚点变化)、onCreatedNavigationTarget(新标签/窗口被创建为导航目标)。
chrome.declarativeNetRequest
需 declarativeNetRequest(可阻塞)或 declarativeNetRequestWithHostAccess(仅在有 host 权限的请求上生效)。声明式拦截/改写网络请求,无需读取请求内容,性能与隐私优于已废弃的阻塞式 webRequest。
规则数量上限:静态规则跨启用 ruleset 保底 30,000 条(GUARANTEED_MINIMUM_STATIC_RULES);动态规则上限 30,000(MAX_NUMBER_OF_DYNAMIC_RULES);会话规则上限 5,000(MAX_NUMBER_OF_SESSION_RULES);静态 ruleset 总数 100,同时启用至多 50。规则 action.type 取值:"block"(拦截)、"redirect"(重定向)、"allow"(放行,优先于 block)、"upgradeScheme"(HTTP 升级 HTTPS)、"modifyHeaders"(改写请求/响应头)。
updateDynamicRules(options)
增删动态规则(跨会话持久),返回 Promise<void>。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options.addRules |
Rule[] |
[] |
新增规则 |
options.removeRuleIds |
number[] |
[] |
按 ID 删除,先删后加 |
// 上下文:Service Worker
await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [1],
addRules: [{
id: 1,
priority: 1,
action: { type: "block" },
condition: {
urlFilter: "||ads.example.com",
resourceTypes: ["script", "image"]
}
}]
});
getDynamicRules(filter?)
返回当前动态规则。
// 上下文:Service Worker
const rules = await chrome.declarativeNetRequest.getDynamicRules();
updateSessionRules(options)
增删会话规则(浏览器关闭即清除,不写磁盘),参数同 updateDynamicRules。
// 上下文:Service Worker
await chrome.declarativeNetRequest.updateSessionRules({
addRules: [{
id: 100,
priority: 1,
action: {
type: "modifyHeaders",
requestHeaders: [{ header: "x-tag", operation: "set", value: "ext" }]
},
condition: { urlFilter: "||api.example.com", resourceTypes: ["xmlhttprequest"] }
}]
});
getSessionRules()
返回当前会话规则。
// 上下文:Service Worker
const session = await chrome.declarativeNetRequest.getSessionRules();
静态规则在 manifest 的 declarative_net_request.rule_resources 中以 JSON 文件声明,随扩展打包,适合稳定的大规模过滤列表:
{
"declarative_net_request": {
"rule_resources": [{
"id": "ruleset_1",
"enabled": true,
"path": "rules.json"
}]
}
}
chrome.identity
需 identity 权限。OAuth2 与第三方登录。
getAuthToken(details?)
获取 Google OAuth2 令牌(仅限 Google 账户场景),返回 { token, grantedScopes }。需在 manifest 配 oauth2.client_id 与 scopes。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.interactive |
boolean |
false |
是否允许弹出登录界面 |
details.scopes |
string[] |
manifest 配置 | 覆盖默认 scope |
// 上下文:Service Worker 或页面
const { token } = await chrome.identity.getAuthToken({ interactive: true });
const res = await fetch("https://www.googleapis.com/oauth2/v1/userinfo", {
headers: { Authorization: `Bearer ${token}` }
});
launchWebAuthFlow(details)
通用 OAuth2 流程(适配任意第三方 IdP),打开授权页并在重定向回 getRedirectURL() 时返回最终 URL。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
details.url |
string |
必填 | 授权端点完整 URL |
details.interactive |
boolean |
false |
是否显示授权界面 |
// 上下文:页面(用户手势内)
const redirectUrl = chrome.identity.getRedirectURL();
const authUrl = `https://github.com/login/oauth/authorize?client_id=ID&redirect_uri=${encodeURIComponent(redirectUrl)}`;
const resultUrl = await chrome.identity.launchWebAuthFlow({
url: authUrl,
interactive: true
});
const code = new URL(resultUrl).searchParams.get("code");
getRedirectURL(path?)
同步返回 https://<extension-id>.chromiumapp.org/<path> 形式的回调 URL,用于在 IdP 注册 redirect_uri。
// 上下文:任意
console.log(chrome.identity.getRedirectURL("cb"));
// https://abcdefg.chromiumapp.org/cb
chrome.offscreen
需 offscreen 权限。MV3 的 SW 无 DOM、无 window,本 API 创建隐藏文档以执行需要 DOM 的操作(解析 HTML、读剪贴板、播放音频等)。同一时刻只能有一个 offscreen 文档(split 模式下普通与隐身各一个)。
createDocument(parameters)
创建 offscreen 文档,加载完成后 resolve。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
parameters.url |
string |
必填 | 扩展内 HTML 相对路径 |
parameters.reasons |
string[] |
必填 | 用途枚举,见下 |
parameters.justification |
string |
必填 | 给审核与用户看的理由说明 |
reasons 取值:"AUDIO_PLAYBACK"(播放音频)、"CLIPBOARD"(读写剪贴板)、"DOM_PARSER"(用 DOMParser)、"DOM_SCRAPING"(解析抓取 DOM)、"BLOBS"(创建对象 URL)、"IFRAME_SCRIPTING"、"USER_MEDIA"、"DISPLAY_MEDIA"、"WEB_RTC"、"LOCAL_STORAGE"、"WORKERS"、"BATTERY_STATUS"、"MATCH_MEDIA"、"GEOLOCATION"、"TESTING"。
// 上下文:Service Worker
async function ensureOffscreen() {
const existing = await chrome.runtime.getContexts({
contextTypes: ["OFFSCREEN_DOCUMENT"]
});
if (existing.length) return; // 已存在则跳过,避免重复创建报错
await chrome.offscreen.createDocument({
url: "offscreen.html",
reasons: ["DOM_PARSER"],
justification: "解析远程返回的 HTML 片段"
});
}
// 错误:未判重,第二次创建直接抛 "Only a single offscreen document may be created"
// await chrome.offscreen.createDocument({...}); // 错误:重复创建
closeDocument()
关闭当前 offscreen 文档,无参数,返回 Promise<void>。
// 上下文:Service Worker
await chrome.offscreen.closeDocument();
hasDocument()
返回是否存在 offscreen 文档。注意:此为旧式判断,新代码推荐 chrome.runtime.getContexts。
// 上下文:Service Worker
if (!(await chrome.offscreen.hasDocument())) {
await chrome.offscreen.createDocument({ /* ... */ });
}
chrome.i18n
无需 permission,但需建立 _locales/<lang>/messages.json 并在 manifest 设 default_locale。
getMessage(messageName, substitutions?)
同步返回当前 UI 语言对应的本地化字符串。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
messageName |
string |
必填 | messages.json 中的键名 |
substitutions |
string | string[] |
无 | 替换 $1-$9 占位符,最多 9 个 |
// 上下文:任意
const title = chrome.i18n.getMessage("appTitle");
const greet = chrome.i18n.getMessage("greet", ["小明"]); // "你好,小明"
_locales/zh_CN/messages.json:
{
"appTitle": { "message": "我的扩展" },
"greet": { "message": "你好,$1", "placeholders": {} }
}
getUILanguage()
同步返回浏览器 UI 语言(如 "zh-CN"),无参数。
// 上下文:任意
console.log(chrome.i18n.getUILanguage());
detectLanguage(text)
检测文本语言,返回 { isReliable, languages: [{ language, percentage }] }。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
text |
string |
必填 | 待检测文本 |
// 上下文:任意
const r = await chrome.i18n.detectLanguage("这是一段中文");
console.log(r.languages[0].language); // "zh"
chrome.downloads
需 downloads 权限。
download(options)
发起下载,返回 downloadId。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options.url |
string |
必填 | 下载地址 |
options.filename |
string |
服务器推断 | 相对默认下载目录的路径 |
options.saveAs |
boolean |
false |
是否弹「另存为」 |
options.conflictAction |
string |
"uniquify" |
"uniquify"/"overwrite"/"prompt" |
// 上下文:Service Worker 或页面
const id = await chrome.downloads.download({
url: "https://example.com/report.pdf",
filename: "reports/report.pdf"
});
其它常用:search(query) 查询下载项、cancel(id) 取消、pause(id) / resume(id) 暂停恢复、onChanged 监听状态变化。
chrome.bookmarks
需 bookmarks 权限。
create(bookmark)
创建书签或文件夹(省略 url 即文件夹),返回 BookmarkTreeNode。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
bookmark.parentId |
string |
「其他书签」 | 父节点 ID |
bookmark.title |
string |
"" |
标题 |
bookmark.url |
string |
无(=文件夹) | 链接地址 |
bookmark.index |
number |
末尾 | 在父节点中的位置 |
// 上下文:Service Worker 或页面
const node = await chrome.bookmarks.create({
title: "Chrome 扩展文档",
url: "https://developer.chrome.com/docs/extensions"
});
其它常用:search(query) 搜索、getTree() 取完整树、remove(id) / removeTree(id) 删除、update(id, changes) 改标题或 URL、move(id, dest) 移动。
chrome.history
需 history 权限。
search(query)
搜索浏览历史,返回 HistoryItem[]。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
query.text |
string |
""(全部) |
模糊匹配 URL/标题 |
query.startTime |
number |
24 小时前 | 起始时间 epoch ms |
query.endTime |
number |
现在 | 结束时间 |
query.maxResults |
number |
100 | 返回上限 |
// 上下文:Service Worker 或页面
const items = await chrome.history.search({
text: "github",
maxResults: 20
});
其它常用:addUrl({ url }) 添加访问记录、deleteUrl({ url }) 删除某 URL、deleteRange({ startTime, endTime }) 按时间段删、getVisits({ url }) 取访问明细、onVisited / onVisitRemoved 事件。
最佳实践
1. 批量读写 storage,避免逐条操作
set/get 接受多键对象,一次调用比循环多次调用快得多,且能规避 sync 区每分钟 120 次的写频率上限。
// 推荐:单次写多键
await chrome.storage.local.set({ a: 1, b: 2, c: 3 });
// 错误:循环逐条,慢且易触限流
// for (const [k, v] of entries) await chrome.storage.sync.set({ [k]: v }); // 错误:高频写
2. tabs.query 必须带条件,杜绝全表扫描
无条件 query({}) 返回所有窗口所有标签,浪费且需更宽权限。绝大多数场景只需当前激活标签。
// 推荐:精确锁定
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
3. 用 alarms 替代 setTimeout/setInterval
MV3 的 SW 空闲约 30 秒后被回收,定时器随之失效。持久定时任务一律用 chrome.alarms,并记得最小周期 0.5 分钟。
// 推荐
chrome.runtime.onInstalled.addListener(() => {
chrome.alarms.create("poll", { periodInMinutes: 5 });
});
chrome.alarms.onAlarm.addListener((a) => a.name === "poll" && poll());
4. 异步消息回复显式 return true,或返回 Promise
onMessage 监听器若在异步操作后调用 sendResponse,未 return true 会导致通道提前关闭、响应丢失。Chrome 148+ 可直接返回 Promise 替代。
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
fetchData().then(sendResponse);
return true; // 推荐:保持通道
});
5. offscreen / 资源创建前先判重
offscreen 文档全局仅一个,重复 createDocument 直接抛错。创建前用 getContexts 检查,contextMenus 则在 onInstalled 中创建以避免 SW 多次唤醒重复添加。
const has = (await chrome.runtime.getContexts({
contextTypes: ["OFFSCREEN_DOCUMENT"]
})).length > 0;
if (!has) await chrome.offscreen.createDocument({ /* ... */ });
6. 优先 declarativeNetRequest 而非阻塞式 webRequest
MV3 已移除阻塞式 webRequest。请求拦截/改写改用声明式规则,性能更高且无需读取请求体。频繁变动的规则用 updateDynamicRules,临时规则用会话规则。
await chrome.declarativeNetRequest.updateDynamicRules({
addRules: [{ id: 1, priority: 1, action: { type: "block" },
condition: { urlFilter: "||tracker.com", resourceTypes: ["script"] } }]
});
7. 运行时权限按需申请,缩小默认权限面
把非核心权限放进 optional_permissions,在用户触发对应功能时用 permissions.request(须在手势内)申请,既过审更顺利,也减少安装时的权限警告。
btn.onclick = async () => {
if (await chrome.permissions.request({ permissions: ["cookies"] })) {
readCookies();
}
};
常见陷阱
陷阱 1:executeScript 的 func 无法访问外部闭包变量
- 现象:
func: () => useExternal在目标页执行时报useExternal is not defined。 - 原因:
func会被序列化为字符串发送到目标页进程执行,与 SW 作用域完全隔离,闭包不跨进程。 - 解决:所有外部数据通过
args数组传入,且必须 JSON 可序列化。
const value = 42;
await chrome.scripting.executeScript({
target: { tabId },
func: (v) => { document.title = v; }, // 通过参数接收
args: [value]
});
陷阱 2:tabs.sendMessage 目标无内容脚本时抛错
- 现象:调用
chrome.tabs.sendMessage抛Could not establish connection. Receiving end does not exist. - 原因:目标标签尚未注入内容脚本(如
chrome://页、刚打开未匹配的页、CS 注入失败)。 - 解决:用
try/catch包裹;或先executeScript确保 CS 存在,再发消息。
try {
await chrome.tabs.sendMessage(tabId, { ping: 1 });
} catch {
await chrome.scripting.executeScript({ target: { tabId }, files: ["cs.js"] });
await chrome.tabs.sendMessage(tabId, { ping: 1 });
}
陷阱 3:storage.sync 静默超配额导致写入失败
- 现象:写入大对象后读回为空或报错,但小数据正常。
- 原因:
sync单项上限 8 KB、总量 100 KB、最多 512 项,超限的set会通过runtime.lastError报错而非抛异常,容易被忽略。 - 解决:大数据放
local(10 MB);sync只存小配置;写前用getBytesInUse估算,Promise 风格用try/catch捕获超配额错误。
try {
await chrome.storage.sync.set({ bigBlob: hugeString }); // 可能超 8KB/项
} catch (e) {
console.warn("sync 超配额,降级到 local:", e.message);
await chrome.storage.local.set({ bigBlob: hugeString });
}
陷阱 4:sidePanel.open / permissions.request 在非手势上下文被拒
- 现象:在
alarm、onMessage、页面加载等非用户手势回调中调用,方法静默失败或报错。 - 原因:这两个 API 出于安全要求必须由用户手势(点击、快捷键)直接触发。
- 解决:只在
action.onClicked、commands.onCommand、DOMclick等手势回调内调用,且避免在手势回调里先await其它异步操作再调用(部分浏览器会判定手势已失效)。