4. 扩展核心API(下):chrome.contextMenus、chrome.notifications、chrome.commands API详解与实战

好,咱们接着聊。上一节我们把消息系统和存储讲透了,这一节要啃的这三个API,说白了就是让你的插件真正“活”起来的关键。它们分别负责:右键菜单、系统通知、键盘快捷键。这三个东西用好了,用户体验能直接上一个台阶。

4.1 chrome.contextMenus:右键菜单的深度定制

右键菜单,是浏览器里最自然的交互入口之一。用户选中一段文字、一张图片,或者在一个空白页上,右键一点,你的插件就能出现在那里。我个人习惯把最常用的功能放在右键菜单里,因为用户不需要打开弹窗,不需要点图标,一步到位。

4.1.1 基础用法:创建菜单项

核心API就一个:chrome.contextMenus.create。你需要在后台脚本(Service Worker)里调用它。

// background.js
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: 'translate-selection',
    title: '翻译选中文字',
    contexts: ['selection'] // 只在选中文本时显示
  });
});

// 监听点击事件
chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === 'translate-selection') {
    console.log('用户选中了:', info.selectionText);
    // 这里可以调用翻译API
  }
});

嗯,这里要注意:contexts 参数决定了菜单在什么场景下出现。常用的有 selection(选中文本)、image(图片)、link(链接)、page(页面空白处)。

我的小技巧: 如果你想让菜单在“选中图片”和“选中文字”时都出现,可以传一个数组:contexts: ['selection', 'image']。但别贪多,太多场景反而让用户困惑。

4.1.2 进阶:多级菜单与动态更新

有时候一个菜单不够用,你需要子菜单。比如“分享到”下面再分“微信”、“微博”、“QQ”。

chrome.contextMenus.create({
  id: 'share-parent',
  title: '分享到',
  contexts: ['selection']
});

chrome.contextMenus.create({
  id: 'share-wechat',
  parentId: 'share-parent',
  title: '微信',
  contexts: ['selection']
});

chrome.contextMenus.create({
  id: 'share-weibo',
  parentId: 'share-parent',
  title: '微博',
  contexts: ['selection']
});

动态更新菜单也很常见。比如根据用户登录状态,显示“登录”或“退出”。用 chrome.contextMenus.update 就能搞定。

我曾经踩过的坑: 菜单ID必须全局唯一。如果你在 onInstalled 里重复创建相同ID的菜单,Chrome会报错。我的做法是:每次更新前先 chrome.contextMenus.removeAll() 清空,再重新创建。虽然粗暴,但稳。

4.2 chrome.notifications:让通知变得优雅

通知,是插件和用户保持联系的桥梁。下载完成、任务提醒、新消息推送……但通知也是一把双刃剑:用好了是贴心,用滥了就是骚扰。

4.2.1 创建通知

API很简单,一个 chrome.notifications.create 搞定。但参数里有个坑,我当年就栽过。

chrome.notifications.create({
  type: 'basic',
  iconUrl: 'icons/icon128.png',
  title: '下载完成',
  message: '你的文件已经准备好了',
  buttons: [
    { title: '打开文件' },
    { title: '忽略' }
  ],
  priority: 2
}, (notificationId) => {
  console.log('通知已创建,ID:', notificationId);
});

为什么说这里有坑?iconUrl 必须是一个可访问的本地路径,或者是一个完整的URL。如果你用相对路径,比如 icon.png,它会在当前页面里找,但后台脚本没有页面上下文,所以会失败。我建议用绝对路径,或者用 chrome.runtime.getURL('icons/icon128.png') 来生成。

4.2.2 通知的交互

用户点击通知或者按钮,你需要响应。监听 chrome.notifications.onClickedchrome.notifications.onButtonClicked 即可。

chrome.notifications.onButtonClicked.addListener((notificationId, buttonIndex) => {
  if (buttonIndex === 0) {
    // 用户点击了“打开文件”
    chrome.tabs.create({ url: 'https://example.com/file' });
  }
});
核心原则: 通知不是用来刷存在感的。只在用户真正需要知道的时候才发。比如下载完成、错误提醒、定时任务。别为了推广而发通知,用户会直接卸载你的插件。

4.3 chrome.commands:键盘快捷键的力量

快捷键,是高级用户的标配。一个熟练的用户,能用快捷键完成90%的操作。我个人觉得,给插件配快捷键,是性价比最高的优化之一。

4.3.1 声明快捷键

快捷键需要在 manifest.json 里声明,而不是在代码里动态绑定。

{
  "commands": {
    "toggle-feature": {
      "suggested_key": {
        "default": "Ctrl+Shift+Y",
        "mac": "Command+Shift+Y"
      },
      "description": "切换功能开关"
    },
    "open-popup": {
      "suggested_key": {
        "default": "Ctrl+Shift+U",
        "mac": "Command+Shift+U"
      },
      "description": "打开弹窗"
    }
  }
}

注意:suggested_key 只是“建议键位”,用户可以在 chrome://extensions/shortcuts 里自行修改。所以你的代码里不要硬编码快捷键,而是通过命令ID来响应。

4.3.2 响应快捷键

在后台脚本里监听 chrome.commands.onCommand 事件。

chrome.commands.onCommand.addListener((command) => {
  if (command === 'toggle-feature') {
    // 执行切换逻辑
    console.log('快捷键触发了切换功能');
  } else if (command === 'open-popup') {
    // 打开弹窗
    chrome.action.openPopup();
  }
});
避坑指南: 我曾经犯过一个错误:把快捷键绑定到了 Ctrl+S 上。结果用户一按,页面保存和插件功能同时触发,乱成一锅粥。记住,不要覆盖浏览器原生快捷键(比如 Ctrl+T 新建标签页、Ctrl+W 关闭标签页)。建议用 Ctrl+Shift+字母Ctrl+Alt+字母 的组合。

4.4 实战:一个完整的“划词翻译+通知”插件

光说不练假把式。咱们把这三个API串起来,做一个简单的划词翻译插件。用户选中文字,右键点击“翻译”,然后弹出一个通知显示结果。

// background.js
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: 'translate',
    title: '翻译选中文字',
    contexts: ['selection']
  });
});

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === 'translate') {
    const text = info.selectionText;
    // 模拟翻译结果(实际项目中调用翻译API)
    const translated = `[翻译] ${text}`;

    chrome.notifications.create({
      type: 'basic',
      iconUrl: chrome.runtime.getURL('icons/icon128.png'),
      title: '翻译结果',
      message: translated,
      priority: 1
    });
  }
});

// 快捷键:Ctrl+Shift+T 直接翻译当前选中的文字
chrome.commands.onCommand.addListener((command) => {
  if (command === 'translate-selection') {
    // 通过 content script 获取选中文字
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      chrome.tabs.sendMessage(tabs[0].id, { action: 'getSelection' });
    });
  }
});

这个例子虽然简单,但把三个API的核心用法都覆盖了。你可以在实际项目中扩展它,比如加上语言检测、历史记录、语音朗读等功能。

总结一下这三个API的定位:

  • contextMenus:让用户“右键即用”,适合高频操作
  • notifications:让用户“被动知晓”,适合异步提醒
  • commands:让用户“指尖操作”,适合效率工具

三者结合,你的插件就能覆盖用户从“发现”到“使用”再到“习惯”的全流程。

扩展核心API(下)知识体系 chrome.contextMenus chrome.notifications chrome.commands create() onClicked update/remove create() onClicked onButtonClicked manifest声明 onCommand 快捷键冲突 实战:划词翻译 + 通知 + 快捷键

好了,这一节的内容就到这里。这三个API单独看都不难,但组合起来能玩出很多花样。你想想看,右键菜单 + 通知 + 快捷键,几乎覆盖了用户所有可能的操作路径。下次你设计插件时,不妨先问问自己:用户最常用的三个操作是什么?然后分别用这三个API去承载它们。