Electron 如何自定义菜单?这篇帮你实现原生体验!

目录

  • [1. 概述](#1. 概述 "#1-%E6%A6%82%E8%BF%B0")
  • [2. Menu 类](#2. Menu 类 "#2-menu-%E7%B1%BB")
  • [3. MenuItem 类](#3. MenuItem 类 "#3-menuitem-%E7%B1%BB")
  • [4. 菜单类型](#4. 菜单类型 "#4-%E8%8F%9C%E5%8D%95%E7%B1%BB%E5%9E%8B")
  • [5. 角色 (Roles)](#5. 角色 (Roles) "#5-%E8%A7%92%E8%89%B2-roles")
  • [6. 快捷键 (Accelerator)](#6. 快捷键 (Accelerator) "#6-%E5%BF%AB%E6%8D%B7%E9%94%AE-accelerator")
  • [7. 实际应用示例](#7. 实际应用示例 "#7-%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E7%A4%BA%E4%BE%8B")
  • [8. 平台差异](#8. 平台差异 "#8-%E5%B9%B3%E5%8F%B0%E5%B7%AE%E5%BC%82")

1. 概述

css 复制代码
Menu(菜单)
  │
  ├── MenuItem(菜单项)
  ├── MenuItem(菜单项)
  │     │
  │     └── Submenu(子菜单)
  │           ├── MenuItem
  │           └── MenuItem
  ├── MenuItem(复选框)
  └── MenuItem(分隔线)

菜单类型

类型 说明 使用场景
应用菜单 窗口顶部的菜单栏 主菜单
上下文菜单 右键点击弹出的菜单 操作菜单
子菜单 菜单项的嵌套菜单 组织功能

2.1 创建菜单

javascript 复制代码
const { Menu, MenuItem } = require('electron')

const menu = new Menu()

2.2 设置应用菜单

javascript 复制代码
const template = [
  {
    label: '文件',
    submenu: [
      { label: '新建', accelerator: 'CmdOrCtrl+N', click: () => {} },
      { type: 'separator' },
      { label: '退出', accelerator: 'CmdOrCtrl+Q', click: () => app.quit() }
    ]
  },
  {
    label: '编辑',
    submenu: [
      { role: 'undo' },
      { role: 'redo' },
      { type: 'separator' },
      { role: 'cut' },
      { role: 'copy' },
      { role: 'paste' }
    ]
  }
]

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

2.3 静态方法

方法 说明
Menu.setApplicationMenu(menu) 设置应用菜单
Menu.getApplicationMenu() 获取应用菜单
Menu.buildFromTemplate(template) 从模板构建菜单
Menu.sendActionToFirstResponder(action) macOS 专用

2.4 实例方法

方法 说明
menu.popup([options]) 弹出菜单(作为上下文菜单)
menu.closePopup() 关闭弹出的菜单
menu.append(menuItem) 追加菜单项
menu.insert(pos, menuItem) 在指定位置插入菜单项
menu.getMenuItemById(id) 根据 ID 获取菜单项
javascript 复制代码
menu.popup({
  window: BrowserWindow,      // 指定窗口,默认聚焦窗口
  x: 100,                     // x 坐标
  y: 200,                     // y 坐标
  positioningItem: 0,         // macOS:在鼠标位置高亮的项
  callback: () => {}          // 菜单关闭后的回调
})

2.6 实例事件

事件 说明
menu-will-show 调用 popup() 时触发
menu-will-close 菜单关闭时触发

3.1 基本创建

javascript 复制代码
const { MenuItem } = require('electron')

const item = new MenuItem({
  label: '新建文件',
  click: () => {
    console.log('点击了新建文件')
  }
})

3.2 构造函数选项

选项 类型 说明
label string 菜单项文本
click function 点击回调
role string 预定义角色
type string 菜单项类型
accelerator Accelerator 快捷键
icon NativeImage/string 图标
enabled boolean 是否启用
visible boolean 是否可见
checked boolean 是否选中(checkbox/radio)
submenu Menu/MenuItem[] 子菜单
id string 唯一标识
before string[] 在某项之前插入
after string[] 在某项之后插入

3.3 实例属性

属性 说明 可修改
id 唯一标识
label 显示文本
click 点击函数
type 类型
role 角色
accelerator 快捷键
icon 图标
sublabel 子标签 (macOS)
toolTip 提示文本 (macOS)
enabled 是否启用
visible 是否可见
checked 是否选中
submenu 子菜单

4. 菜单类型

4.1 普通菜单项

javascript 复制代码
{
  label: '新建',
  click: () => createNewFile()
}

4.2 分隔线

javascript 复制代码
{ type: 'separator' }

4.3 子菜单

javascript 复制代码
{
  label: '文件',
  submenu: [
    { label: '新建', click: () => {} },
    { label: '打开', click: () => {} }
  ]
}

4.4 复选框菜单项

javascript 复制代码
{
  label: '自动保存',
  type: 'checkbox',
  checked: true,
  click: (menuItem) => {
    console.log('自动保存:', menuItem.checked)
  }
}

4.5 单选菜单项

javascript 复制代码
{
  label: '简体中文',
  type: 'radio',
  checked: true,
  click: () => setLanguage('zh')
},
{
  label: 'English',
  type: 'radio',
  checked: false,
  click: () => setLanguage('en')
}

4.6 动态构建子菜单

javascript 复制代码
{
  label: '最近文件',
  submenu: [
    { label: 'file1.txt', click: () => openFile('file1.txt') },
    { label: 'file2.txt', click: () => openFile('file2.txt') },
    { type: 'separator' },
    { label: '清除最近文件', role: 'clearRecentDocuments' }
  ]
}

5. 角色 (Roles)

5.1 通用角色

角色 说明
undo 撤销
redo 重做
cut 剪切
copy 复制
paste 粘贴
delete 删除
selectAll 全选
reload 重新加载
forceReload 强制重新加载
toggleDevTools 切换开发者工具
togglefullscreen 切换全屏
resetZoom 重置缩放
zoomIn 放大
zoomOut 缩小
minimize 最小化窗口
close 关闭窗口
quit 退出应用

5.2 macOS 特有角色

角色 说明
about 关于面板
hide 隐藏应用
hideOthers 隐藏其他应用
unhide 显示所有应用
startSpeaking 开始朗读
stopSpeaking 停止朗读
front 窗口前置
appMenu 应用菜单
fileMenu 文件菜单
editMenu 编辑菜单
viewMenu 视图菜单
windowMenu 窗口菜单
help 帮助菜单
services 服务菜单
shareMenu 分享菜单

5.3 使用角色

javascript 复制代码
{
  label: '编辑',
  submenu: [
    { role: 'undo', label: '撤销' },
    { role: 'redo', label: '重做' },
    { type: 'separator' },
    { role: 'cut', label: '剪切' },
    { role: 'copy', label: '复制' },
    { role: 'paste', label: '粘贴' }
  ]
}

⚠️ 注意 :macOS 上使用 role 时,labelaccelerator 会被忽略,会自动使用系统默认值。


6. 快捷键 (Accelerator)

6.1 基本语法

javascript 复制代码
{
  label: '新建',
  accelerator: 'CmdOrCtrl+N',
  click: () => {}
}

6.2 常用快捷键格式

格式 说明 示例
CmdOrCtrl+N 命令/控制 + N Ctrl+N / ⌘N
Cmd+N 仅 macOS ⌘N
Ctrl+Shift+N 控制 + Shift + N Ctrl+Shift+N
Alt+F Alt + F Alt+F
F5 功能键 F5

6.3 修饰键

修饰键 Windows/Linux macOS
Ctrl
Alt
Shift
Cmd / Command
CmdOrCtrl ✅ (使用 ⌘)

6.4 快捷键选项

javascript 复制代码
{
  label: '粘贴',
  accelerator: 'CmdOrCtrl+V',
  acceleratorWorksWhenHidden: false,  // macOS:隐藏时是否生效
  registerAccelerator: true          // 是否注册到系统
}

7. 实际应用示例

7.1 应用菜单完整示例

javascript 复制代码
const { app, Menu, MenuItem, BrowserWindow, shell } = require('electron')

function createMenu() {
  const template = [
    {
      label: '文件',
      submenu: [
        {
          label: '新建窗口',
          accelerator: 'CmdOrCtrl+N',
          click: () => createNewWindow()
        },
        {
          label: '打开文件',
          accelerator: 'CmdOrCtrl+O',
          click: () => openFile()
        },
        {
          label: '保存',
          accelerator: 'CmdOrCtrl+S',
          click: () => saveFile()
        },
        { type: 'separator' },
        {
          label: '导出',
          submenu: [
            { label: '导出为 PDF', click: () => exportPDF() },
            { label: '导出为 Word', click: () => exportWord() }
          ]
        },
        { type: 'separator' },
        {
          label: '退出',
          accelerator: 'CmdOrCtrl+Q',
          click: () => app.quit()
        }
      ]
    },
    {
      label: '编辑',
      submenu: [
        { role: 'undo' },
        { role: 'redo' },
        { type: 'separator' },
        { role: 'cut' },
        { role: 'copy' },
        { role: 'paste' },
        { role: 'delete' },
        { type: 'separator' },
        { role: 'selectAll' }
      ]
    },
    {
      label: '视图',
      submenu: [
        { role: 'reload' },
        { role: 'forceReload' },
        { role: 'toggleDevTools' },
        { type: 'separator' },
        { role: 'resetZoom' },
        { role: 'zoomIn' },
        { role: 'zoomOut' },
        { type: 'separator' },
        { role: 'togglefullscreen' }
      ]
    },
    {
      label: '窗口',
      submenu: [
        { role: 'minimize' },
        { role: 'zoom' },
        { role: 'close' }
      ]
    },
    {
      label: '帮助',
      submenu: [
        {
          label: '关于',
          click: () => showAboutDialog()
        },
        {
          label: '文档',
          click: () => shell.openExternal('https://electronjs.org')
        }
      ]
    }
  ]

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

7.2 自定义上下文菜单

javascript 复制代码
const { Menu } = require('electron')

function showContextMenu(window) {
  const contextMenu = Menu.buildFromTemplate([
    {
      label: '复制',
      accelerator: 'CmdOrCtrl+C',
      role: 'copy'
    },
    {
      label: '粘贴',
      accelerator: 'CmdOrCtrl+V',
      role: 'paste'
    },
    { type: 'separator' },
    {
      label: '全选',
      accelerator: 'CmdOrCtrl+A',
      role: 'selectAll'
    },
    { type: 'separator' },
    {
      label: '开发者选项',
      submenu: [
        {
          label: '刷新',
          accelerator: 'CmdOrCtrl+R',
          role: 'reload'
        },
        {
          label: '开发者工具',
          accelerator: 'F12',
          role: 'toggleDevTools'
        }
      ]
    }
  ])

  contextMenu.popup(window)
}

// 在渲染进程中使用
webContents.on('context-menu', (event, params) => {
  const contextMenu = Menu.buildFromTemplate([
    { label: '复制', role: 'copy', enabled: params.selectionText.length > 0 },
    { label: '粘贴', role: 'paste', enabled: params.editFlags.canPaste },
    { type: 'separator' },
    { label: `选中文字: "${params.selectionText}"` }
  ])
  contextMenu.popup()
})

7.3 动态菜单(根据状态)

javascript 复制代码
function updateMenuState(isLoggedIn, userName) {
  const template = [
    {
      label: '文件',
      submenu: [
        { label: '新建', click: () => {} },
        { label: '打开', click: () => {} }
      ]
    },
    {
      label: '账户',
      submenu: isLoggedIn ? [
        { label: `用户: ${userName}` },
        { label: '退出登录', click: () => logout() }
      ] : [
        { label: '登录', click: () => showLogin() },
        { label: '注册', click: () => showRegister() }
      ]
    }
  ]

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

8. 平台差异

8.1 macOS vs Windows/Linux

特性 macOS Windows/Linux
菜单位置 屏幕顶部(系统级) 窗口顶部
角色默认行为 自动应用 需要手动设置
sublabel 支持 不支持
toolTip 支持 不支持
右键菜单样式 原生样式 Chromium 样式

8.2 Windows/Linux 快捷键前缀

javascript 复制代码
// Windows/Linux 可以用 & 设置快捷键
{
  label: '&File',      // Alt+F 打开文件菜单
  submenu: [
    { label: '&New', accelerator: 'Ctrl+N' },
    { label: '&Open', accelerator: 'Ctrl+O' },
    { label: '&Save', accelerator: 'Ctrl+S' }
  ]
}

// 转义 & 字符
{ label: '&&Settings' }  // 显示为 &Settings

8.3 禁用默认菜单

javascript 复制代码
// 禁用默认菜单
Menu.setApplicationMenu(null)

最佳实践

✅ 推荐做法

javascript 复制代码
// 1. 使用角色而不是手动实现
{ role: 'copy' }  // ✅ 原生体验

// 2. 避免手动实现
{ label: '复制', click: () => document.execCommand('copy') }  // ❌

// 3. 动态菜单使用模板方式
Menu.buildFromTemplate(template)  // ✅

// 4. macOS 考虑系统菜单规范

❌ 避免做法

  1. 不要在 macOS 上手动设置 label/accelerator

    javascript 复制代码
    // macOS 会忽略这些值
    { role: 'copy', label: '复制' }  // ❌
    { role: 'copy' }  // ✅
  2. 不要忘记分隔线类型

    javascript 复制代码
    { type: 'separator' }  // ✅
    { label: '---' }  // ❌
  3. 不要忽略 enabled 状态

    javascript 复制代码
    { label: '删除', enabled: false }  // 禁用时置灰显示

速查表

css 复制代码
Menu.setApplicationMenu(menu)      设置应用菜单
Menu.getApplicationMenu()          获取应用菜单
Menu.buildFromTemplate(template)   从模板构建菜单
css 复制代码
menu.popup(options)                弹出菜单
menu.closePopup()                  关闭菜单
menu.append(item)                  添加菜单项
menu.insert(pos, item)             插入菜单项
menu.getMenuItemById(id)           获取菜单项
css 复制代码
normal                              普通菜单项
separator                          分隔线
submenu                            子菜单
checkbox                           复选框
radio                              单选按钮

常用角色

bash 复制代码
undo, redo, cut, copy, paste       编辑操作
reload, toggleDevTools             视图操作
minimize, close                    窗口操作
togglefullscreen                   全屏操作

文档基于 Electron v28+ Menu/MenuItem API 编写

相关推荐
七十二時_阿川4 小时前
Electron App 速查表:生命周期事件、方法、平台差异
前端·electron
七十二時_阿川4 小时前
Electron 多显示器开发?这篇帮你搞定屏幕坐标与窗口定位!
前端·electron
七十二時_阿川4 小时前
Electron Tray API 详解:托盘图标、右键菜单、气泡通知
前端·electron
番茄炒韭菜4 小时前
windows10下安装mise
前端
用户938515635075 小时前
AI全栈前端实战|DeepSeek + CC插件,1小时产出高质量外卖App落地页
前端
AI2中文网5 小时前
App Inventor 2 向心力实验App - 探究向心力F与角速度ω、半径r、质量m的关系
前端·javascript·r语言
程序软件分享5 小时前
vue多语言交易所系统/期货/合约交易/质押生息/盲盒/挖矿/跟单源码
前端·javascript·vue.js·期货平台源码
悟空瞎说5 小时前
【前端视角学 Rust】1.3 一文吃透 Cargo:Rust 的 npm+webpack,新手必懂工程化工具
前端
yingyima5 小时前
Linux Crontab 速查手册:5 个问题直击核心语法与常用场景
前端