首页 / 浏览器扩展开发入门教程 / 快捷键 commands

浏览器扩展开发入门教程

快捷键 commands

本教程共 56 篇 · 第 33 篇 · 更新于 2026-08-13 · 约 7 分钟阅读

commands快捷键_execute_actiononCommandsuggested_keyManifest V3

本节目标:学完你能给扩展绑定键盘快捷键,会用保留命令 _execute_action 一键打开弹出页,也会声明自定义命令并在后台接住 onCommand,还知道组合键有哪些硬性规则。

模块七的最后一节。前面八个界面组件都得动鼠标:点图标、点菜单、点面板。这一节讲怎么让用户完全脱离鼠标——按一下组合键,功能就跑起来。

对高频使用的扩展来说,快捷键的价值被严重低估。翻译、截图、收藏、开关某个功能,这些一天要用十几次的动作,配上快捷键体验完全不一样。

33-1 commands 的两种命令

commands 字段里能声明两类命令,区别很重要:

  • 保留命令:名字以下划线开头,浏览器内置了行为,你只管声明,不用写任何 JS。Manifest V3 里最常用的就是 _execute_action
  • 自定义命令:名字由你定,比如 toggle-feature。浏览器只负责”按键了就通知你”,具体干什么要自己在后台写监听。

先记下这条对照:要打开 popup 用保留命令,要执行逻辑用自定义命令。

33-2 清单声明的完整结构

commands 是清单顶层字段,值是一个对象,每个 key 就是一个命令名:

{
  "manifest_version": 3,
  "name": "快捷键示例",
  "version": "1.0.0",
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "commands": {
    "run-action": {
      "suggested_key": {
        "default": "Ctrl+Shift+Y",
        "mac": "Command+Shift+Y"
      },
      "description": "Run the sample action"
    },
    "toggle-feature": {
      "suggested_key": {
        "default": "Ctrl+Shift+U",
        "mac": "Command+Shift+U"
      },
      "description": "Toggle a feature on or off"
    }
  }
}

每个命令下面两个字段:

  • suggested_key:建议的组合键。default 是通用键位,mac 单独指定 macOS 上的键位(还支持 windowslinuxchromeos)。
  • description:命令的说明文字。它会显示在浏览器的快捷键设置页里,用户看到的就是这句话,所以写清楚点。
Note

suggested_key 字面意思就是”建议”。如果这个组合键已经被浏览器自身或别的扩展占用,你的建议不会生效,命令会处于”未设置”状态,得用户手动去分配。这不是 bug,是设计如此。

description 对自定义命令是必填的。保留命令的 description 可以省略,因为浏览器有默认文案,但我建议还是写上中文说明,用户在设置页看得更明白。

33-3 保留命令 _execute_action

最省事的一个:绑定快捷键到工具栏按钮。按键效果 = 点了一次扩展图标。

{
  "manifest_version": 3,
  "name": "我的扩展",
  "version": "1.0.0",
  "action": {
    "default_popup": "popup.html"
  },
  "commands": {
    "_execute_action": {
      "suggested_key": {
        "default": "Ctrl+Shift+Y",
        "mac": "Command+Shift+Y"
      },
      "description": "打开扩展弹出页"
    }
  }
}

就这样,一行 JS 都不用写。用户按 Ctrl+Shift+Y,popup 就弹出来了。

如果 action 没配 default_popup,那按快捷键就等于触发 chrome.action.onClicked,你在后台监听它即可:

chrome.action.onClicked.addListener((tab) => {
  console.log('图标被点了,或者快捷键被按了', tab.id);
});
Tip

_execute_action 是 Manifest V3 下唯一常用的保留命令。它对应的是清单里的 action 字段,也就是 MV3 唯一的工具栏按钮入口。

33-4 自定义命令 + onCommand 监听

自定义命令的完整链路是两段:清单里声明名字,后台用 chrome.commands.onCommand 接住。

let featureEnabled = false;

chrome.commands.onCommand.addListener(async (command) => {
  if (command === 'run-action') {
    chrome.notifications.create({
      type: 'basic',
      iconUrl: 'images/icon-128.png',
      title: 'Commands API Demo',
      message: 'The "run-action" command was triggered.'
    });
  }

  if (command === 'toggle-feature') {
    featureEnabled = !featureEnabled;
    const state = featureEnabled ? 'ON' : 'OFF';

    chrome.action.setBadgeText({ text: featureEnabled ? 'ON' : '' });
    chrome.action.setBadgeBackgroundColor({ color: '#4688F1' });

    chrome.notifications.create({
      type: 'basic',
      iconUrl: 'images/icon-128.png',
      title: 'Feature Toggled',
      message: `The feature is now ${state}.`
    });
  }
});

回调的第一个参数就是清单里那个命令名字符串。所以命名要有辨识度,别用 cmd1cmd2 这种。

监听器还能拿到第二个参数——当前标签页:

chrome.commands.onCommand.addListener((command, tab) => {
  if (command === 'copy-title') {
    console.log('当前页面标题:', tab.title);
  }
});

这里有一个必须记牢的坑:

Warning

onCommand 监听器必须写在服务工作者(Service Worker)的顶层,不能包在函数里或异步逻辑之后。后台是随时休眠的,用户按快捷键时后台可能已经睡了,只有顶层注册的监听器才能把它唤醒。写错位置的表现是”有时候好使有时候不好使”,特别难查。

还有个坑关于状态。上面例子里的 featureEnabled 是个全局变量,后台一休眠它就丢了。真实项目里请用 chrome.storage 存:

chrome.commands.onCommand.addListener(async (command) => {
  if (command !== 'toggle-feature') return;

  const { enabled = false } = await chrome.storage.local.get('enabled');
  const next = !enabled;

  await chrome.storage.local.set({ enabled: next });
  await chrome.action.setBadgeText({ text: next ? 'ON' : '' });
});

33-5 组合键有硬规则

suggested_key 不能随便填,浏览器有一套限制,不满足就直接加载失败或命令不生效。

必须带修饰键。 组合键里必须包含 CtrlAlt 之一。macOS 上可以用 CommandMacCtrl。单个字母 Y、单个 Shift+Y 都不行。

Shift 只能当副修饰键。 也就是 Ctrl+Shift+Y 合法,Shift+Y 不合法。

Ctrl+Alt 组合要慎用。 部分键盘布局里 Ctrl+Alt 等于 AltGr,容易和其他软件或输入法打架,建议避免。

主键的取值范围有限。 允许的有:A–Z、0–9、CommaPeriodHomeEndPageUpPageDownSpaceInsertDelete、四个方向键,以及媒体键 MediaNextTrackMediaPlayPauseMediaPrevTrackMediaStop

媒体键有个特例:它们不需要修饰键,可以单独绑定。

{
  "commands": {
    "play-pause": {
      "suggested_key": { "default": "MediaPlayPause" },
      "description": "播放或暂停"
    }
  }
}

跨平台键位分开写。 macOS 上的 Ctrl 在物理上是 Control 键,而用户习惯的是 Command。所以给 Mac 单独指定:

{
  "commands": {
    "open-panel": {
      "suggested_key": {
        "default": "Ctrl+Shift+P",
        "mac": "Command+Shift+P",
        "windows": "Ctrl+Shift+P",
        "linux": "Ctrl+Shift+P"
      },
      "description": "打开面板"
    }
  }
}

只写 default 时,macOS 上 Ctrl 会被自动映射成 Command,所以偷懒只写 default 通常也能用。但显式写 mac 更可控。

Note

还有个数量限制:一个扩展可以声明很多命令,但浏览器只会自动分配前四个建议快捷键。超出的命令仍然存在、仍然能被 onCommand 接到,只是默认没有键位,需要用户自己去设置页分配。所以把最重要的命令写在前面。

33-6 全局快捷键 global

默认情况下,快捷键只在浏览器窗口获得焦点时有效。如果你希望用户在别的软件里也能按,加 "global": true

{
  "commands": {
    "quick-capture": {
      "suggested_key": {
        "default": "Ctrl+Shift+1",
        "mac": "Command+Shift+1"
      },
      "description": "快速记录",
      "global": true
    }
  }
}

全局快捷键的限制更严:只支持 Ctrl+Shift+[0–9] 这一类组合(Mac 上是 Command+Shift+[0–9])。写别的组合配 global: true,全局特性不会生效。

而且它是抢占系统级按键,容易和其他软件冲突。我建议只在真正需要”浏览器不在前台也能用”的场景才开,比如快速剪藏工具。

33-7 查询实际绑定:commands.getAll

因为建议键可能落空、用户也可能改键,你在界面上显示”快捷键是 XX”时,不能直接照抄清单里的值,得去查真实生效的绑定。

chrome.commands.getAll() 返回当前所有命令及其实际键位:

async function listCommands() {
  const commands = await chrome.commands.getAll();
  const container = document.getElementById('commands');

  for (const command of commands) {
    const row = document.createElement('div');
    row.className = 'command-row';

    const name = document.createElement('span');
    name.className = 'command-name';
    name.textContent = command.description || command.name;

    const shortcut = document.createElement('kbd');
    shortcut.textContent = command.shortcut || 'Not set';

    row.appendChild(name);
    row.appendChild(shortcut);
    container.appendChild(row);
  }
}

listCommands();

每个 command 对象上有三个字段:name(命令名)、description(说明)、shortcut(实际生效的组合键,未分配时是空字符串)。

这段代码放在 popup 里最合适——用户打开弹出页就能看到自己的快捷键清单。注意用 createElement + textContent 拼 DOM,别用 innerHTML 拼字符串,这是扩展里应有的安全习惯。

33-8 让用户改键

浏览器提供了统一的快捷键管理页:chrome://extensions/shortcuts。用户在这里能看到所有扩展的命令,并逐个改键、切换全局范围。

你可以在 popup 里放个入口引导用户过去:

document.getElementById('edit-shortcuts').addEventListener('click', () => {
  chrome.tabs.create({ url: 'chrome://extensions/shortcuts' });
});
Tip

用户手动设置的键位优先级高于 suggested_key。他改过之后,你在清单里改建议值也不会覆盖他的选择——这是好事,别试图绕过。

33-9 小结:模块七收尾

快捷键这一节的骨架很短:清单 commands 里声明命令 → 保留命令 _execute_action 零代码打开 popup → 自定义命令在服务工作者顶层用 onCommand 接住 → suggested_key 遵守修饰键和主键规则 → 用 getAll() 查真实绑定、引导用户去 chrome://extensions/shortcuts 改键。

到这里,模块七的八个界面组件全部讲完了:popup 弹出页、options 选项页、右键菜单、地址栏 omnibox、侧边栏、通知、devtools 面板、快捷键。你现在手上有一整套”和用户打交道”的工具,可以按交互形态挑合适的那个:

  • 快速操作 → popup + 快捷键
  • 长期伴随 → 侧边栏
  • 设置项 → options 页
  • 页面内右键 → contextMenus
  • 跳出浏览器提醒 → notifications
  • 前端调试工具 → devtools 面板

界面搭好了,接下来该往里面填能力。下一个模块进入常用 API,从最基础的 storage 存储开始——你的扩展怎么把数据存下来、跨设备同步、并在变化时收到通知。