人人都会AI编程

Menu 应用菜单、上下文菜单

更新时间:2026-07-11

菜单是桌面应用最基本的交互组件之一。Electron 通过 Menu 模块提供了完整的原生菜单能力,包括窗口顶部的应用菜单和通过右键触发的上下文菜单。掌握菜单的创建与使用,是让你的应用融入操作系统习惯的重要一步。

应用菜单(Application Menu)

应用菜单通常位于窗口顶部:在 Windows 和 Linux 上它直接嵌在窗口标题栏下方,在 macOS 上则显示在屏幕顶端系统菜单栏。Electron 使用模板(template)的方式定义菜单结构,这种方式直观且易于维护。

创建并设置应用菜单

菜单的核心是一个 JSON 数组,每一项代表一个菜单项,可以嵌套子菜单。最基本的结构如下:

const { app, Menu, BrowserWindow } = require('electron');

const template = [
  {
    label: '文件',
    submenu: [
      { label: '新建', accelerator: 'CmdOrCtrl+N', click: () => { /* 新建操作 */ } },
      { label: '打开', accelerator: 'CmdOrCtrl+O', click: () => { /* 打开操作 */ } },
      { type: 'separator' },
      { role: 'quit', label: '退出' }
    ]
  },
  {
    label: '编辑',
    submenu: [
      { role: 'undo', label: '撤销' },
      { role: 'redo', label: '重做' },
      { type: 'separator' },
      { role: 'cut', label: '剪切' },
      { role: 'copy', label: '复制' },
      { role: 'paste', label: '粘贴' }
    ]
  },
  {
    label: '查看',
    submenu: [
      { role: 'reload', label: '重新加载' },
      { role: 'toggleDevTools', label: '开发者工具' }
    ]
  }
];

const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

要点说明:

  • accelerator:设置快捷键,CmdOrCtrl 会自动根据平台识别为 macOS 的 Cmd 或 Windows/Linux 的 Ctrl。常用组合如 CmdOrCtrl+SShift+CmdOrCtrl+I 等。
  • role:Electron 提供的内置角色,可以直接赋予菜单项预定义的行为,例如 copypastereloadtoggleDevTools 等。使用 role 的好处是自动启用本地化文本和与系统行为的完美兼容,特别建议在标准菜单项上使用。
  • click:自定义处理函数,当用户点击菜单项时触发。函数的参数包含 menuItembrowserWindowevent,你可以通过 browserWindow 与当前窗口交互。
  • type:菜单项类型,可选值有 normalseparatorsubmenucheckboxradio,默认是 normal。分隔符可以用来视觉上分组。

平台差异化处理

macOS 的应用菜单有一些特殊约定:最左侧的第一个菜单项通常是应用名(如你的应用名称),里面包含“关于”、“偏好设置”、“服务”、“退出”等。我们可以利用条件判断和 role: 'appMenu' 来适配:

const isMac = process.platform === 'darwin';

const template = [
  // macOS 专属应用菜单
  ...(isMac ? [{
    label: app.name,
    submenu: [
      { role: 'about', label: '关于' },
      { type: 'separator' },
      { role: 'services', label: '服务' },
      { type: 'separator' },
      { role: 'hide', label: '隐藏' },
      { role: 'hideOthers', label: '隐藏其他' },
      { role: 'unhide', label: '显示全部' },
      { type: 'separator' },
      { role: 'quit', label: '退出' }
    ]
  }] : []),
  // 文件、编辑等菜单...
];

这样的模板保证在 macOS 上拥有符合系统习惯的菜单,而在 Windows/Linux 上自动跳过。

注意: 当你通过 Menu.setApplicationMenu(menu) 设置菜单后,它会应用到应用的所有窗口。如果需要在某些窗口上显示不同的菜单,可以在创建 BrowserWindow 后使用 win.setMenu(menu) 单独设置,但此时整个应用菜单会被覆盖;更常见的做法是保持一个全局菜单,通过 click 回调获取当前焦点窗口来执行操作。

上下文菜单(Context Menu)

上下文菜单即右键菜单,用户期望在特定区域点击右键时弹出相关操作。Electron 中实现上下文菜单通常有两种方式:在渲染进程中通过 window.addEventListener('contextmenu', ...) 处理,或通过 IPC 让主进程集中管理。出于安全性和推荐的架构,我们应当避免在渲染进程中直接使用 remote 模块(已被废弃),转而使用预加载脚本暴露安全的接口。

主进程端弹出菜单

首先在主进程(或通过 IPC 调用的处理函数)中构建并弹出菜单。这里我们设计一个通用方法,通过 IPC 接收菜单模板并显示:

// main.js 主进程
const { ipcMain, Menu, BrowserWindow } = require('electron');

ipcMain.on('show-context-menu', (event, template) => {
  const menu = Menu.buildFromTemplate(template);
  menu.popup(BrowserWindow.fromWebContents(event.sender));
});

渲染进程触发

在渲染进程的某个元素上监听 contextmenu 事件,阻止默认行为,然后通过预加载脚本暴露的 API 发送自定义菜单模板给主进程。

预加载脚本暴露方法:

// preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  showContextMenu: (template) => ipcRenderer.send('show-context-menu', template)
});

渲染进程代码(HTML 或 JS):

// 在某个 DOM 元素上绑定
document.getElementById('editable-area').addEventListener('contextmenu', (e) => {
  e.preventDefault();
  // 可以基于选区、位置等动态生成菜单
  const template = [
    { label: '剪切', role: 'cut' },
    { label: '复制', role: 'copy' },
    { label: '粘贴', role: 'paste' },
    { type: 'separator' },
    {
      label: '清空内容',
      click: () => {
        document.getElementById('editable-area').innerHTML = '';
      }
    }
  ];
  window.electronAPI.showContextMenu(template);
});

关键细节:

  • Menu.popup() 会自动在鼠标当前位置弹出菜单,也可以传入 x, y 坐标精确控制位置。
  • 如果菜单模板中某个菜单项的 click 需要在渲染进程上下文中执行(比如修改 DOM),可以继续使用 IPC 回传信号,或者直接在 click 函数中通过 webContents.executeJavaScript 在渲染进程执行代码。但更简洁的做法是使用 role 处理标准操作,自定义逻辑则在菜单项的 click 中发送另一个 IPC 消息到渲染进程,触发前端函数。

动态上下文菜单

上下文菜单的一大优势是可以根据用户的操作上下文动态调整。比如在文本编辑器里,有选区时显示“复制”、无选区时将其禁用;在文件列表里,对文件和文件夹显示不同的右键菜单。你可以在构建模板数组时用条件语句轻松实现:

const selectedText = window.getSelection().toString();
const template = [
  { label: '复制', role: 'copy', enabled: !!selectedText },
  { label: '剪切', role: 'cut', enabled: !!selectedText },
  { type: 'separator' },
  { label: '全选', role: 'selectAll' }
];
window.electronAPI.showContextMenu(template);

这样就把菜单与前端状态紧密联系了起来,体验非常自然。

实用技巧

  • 快捷键提示:在菜单项的 accelerator 文字中,Electron 会自动在菜单右侧显示快捷键组合,为高级用户提供操作效率。
  • 禁用与隐藏:通过 enabled: false 让菜单项变灰,visible: false 则彻底隐藏,可根据应用逻辑实时更新(注意:一旦菜单赋值给 Menu.setApplicationMenu() 后,要动态更新菜单项状态需要重新构建菜单并设置,或者保持菜单引用并调用 menuItem.enabled = value)。
  • 菜单中的图标:Electron 的 Menu 不支持直接在每个菜单项中嵌入图标(macOS 系统限制),但你可以通过在模板的 label 中使用特殊字符或 Unicode 符号来模拟简单的视觉标识。
  • 上下文菜单与 DevTools:开发阶段如果默认的右键菜单被 DevTools 接管,可以在 template 中加入 { label: '检查元素', accelerator: 'CmdOrCtrl+Shift+I', click: () => { win.webContents.toggleDevTools(); } } 这样的自定义项来保留调试入口。

通过以上方式,你的应用就可以拥有完全原生体验的菜单交互,无论是顶部的功能栏还是右键唤起的快捷操作,都能让用户上手即用,零学习成本。