适配鸿蒙PC Electron 中文菜单栏实现完全指南
Electron 作为跨平台桌面应用框架,其菜单系统是连接原生系统体验与 Web 交互逻辑的核心组件。默认情况下,Electron 应用可能没有菜单栏,或者菜单栏是英文的。为了提供更好的中文用户体验,我们需要:创建完整的中文菜单栏配置符合用户习惯的快捷键适配不同操作系统的菜单规范实现菜单项的功能逻辑Electron 提供了系统预定义的角色,使用role可以自动处理系统级功能:{ label: '复
Electron 中文菜单栏实现完全指南
前言
在开发 Electron 桌面应用时,菜单栏是用户与应用交互的重要界面元素。对于中文用户来说,一个本地化的中文菜单栏能够显著提升用户体验。本文将详细介绍如何在 Electron 应用中实现完整的中文菜单栏,包括跨平台适配、快捷键配置和最佳实践。
鸿蒙PC效果
先来看一下鸿蒙PC效果

背景介绍
Electron 作为跨平台桌面应用框架,其菜单系统是连接原生系统体验与 Web 交互逻辑的核心组件。默认情况下,Electron 应用可能没有菜单栏,或者菜单栏是英文的。为了提供更好的中文用户体验,我们需要:
-
创建完整的中文菜单栏
-
配置符合用户习惯的快捷键
-
适配不同操作系统的菜单规范
-
实现菜单项的功能逻辑
核心概念
菜单类型
Electron 支持三种主要的菜单类型:
| 菜单类型 | 应用场景 | 核心 API |
|---|---|---|
| 应用菜单(Application Menu) | 窗口顶部导航栏 | Menu.setApplicationMenu() |
| 上下文菜单(Context Menu) | 右键点击触发 | Menu.popup() |
| 托盘菜单(Tray Menu) | 系统托盘图标右键菜单 | Tray.setContextMenu() |
本文主要介绍应用菜单的实现。
核心 API
-
Menu.buildFromTemplate(template): 从模板创建菜单实例 -
Menu.setApplicationMenu(menu): 设置应用菜单 -
MenuItem: 单个菜单项,可配置文本、快捷键、点击事件等
实现步骤
步骤 1: 引入 Menu 模块
首先,在主进程文件中引入 Menu 模块:
const { app, BrowserWindow, Menu, dialog } = require('electron');
步骤 2: 创建菜单模板
菜单模板是一个数组,每个元素对应一个顶级菜单项(如"文件"、"编辑"等)。我们使用中文标签来定义菜单项:
const menuTemplate = [
{
label: '文件',
submenu: [
{ label: '新建', accelerator: 'CmdOrCtrl+N', click: () => {} },
{ label: '打开', accelerator: 'CmdOrCtrl+O', click: () => {} },
// ... 更多菜单项
]
},
// ... 更多顶级菜单
];
步骤 3: 构建并设置菜单
使用模板创建菜单实例,并设置为应用菜单:
const menu = Menu.buildFromTemplate(menuTemplate); Menu.setApplicationMenu(menu);
步骤 4: 在应用启动时调用
在 app.whenReady() 中调用菜单创建函数:
app.whenReady().then(() => {
createWindow();
createApplicationMenu(); // 创建菜单栏
});
代码详解
完整实现代码
以下是完整的中文菜单栏实现代码:
// 引入 Menu 模块
const { app, BrowserWindow, Menu, dialog } = require('electron');
let mainWindow;
// 创建应用菜单栏
function createApplicationMenu() {
const menuTemplate = [
{
label: '文件',
submenu: [
{
label: '新建',
accelerator: 'CmdOrCtrl+N',
click: () => {
if (mainWindow) {
mainWindow.webContents.send('menu:new');
}
}
},
{
label: '打开',
accelerator: 'CmdOrCtrl+O',
click: async () => {
if (mainWindow) {
mainWindow.webContents.send('menu:open');
}
}
},
{ type: 'separator' }, // 分隔线
{
label: '保存',
accelerator: 'CmdOrCtrl+S',
click: () => {
if (mainWindow) {
mainWindow.webContents.send('menu:save');
}
}
},
{ type: 'separator' },
{
label: '退出',
accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q',
click: () => {
app.quit();
}
}
]
},
{
label: '编辑',
submenu: [
{ label: '撤销', accelerator: 'CmdOrCtrl+Z', role: 'undo' },
{ label: '重做', accelerator: 'CmdOrCtrl+Shift+Z', role: 'redo' },
{ type: 'separator' },
{ label: '剪切', accelerator: 'CmdOrCtrl+X', role: 'cut' },
{ label: '复制', accelerator: 'CmdOrCtrl+C', role: 'copy' },
{ label: '粘贴', accelerator: 'CmdOrCtrl+V', role: 'paste' },
{ label: '全选', accelerator: 'CmdOrCtrl+A', role: 'selectAll' }
]
},
{
label: '视图',
submenu: [
{ label: '重新加载', accelerator: 'CmdOrCtrl+R', role: 'reload' },
{ label: '强制重新加载', accelerator: 'CmdOrCtrl+Shift+R', role: 'forceReload' },
{ label: '切换开发者工具', accelerator: 'F12', role: 'toggleDevTools' },
{ type: 'separator' },
{ label: '实际大小', accelerator: 'CmdOrCtrl+0', role: 'resetZoom' },
{ label: '放大', accelerator: 'CmdOrCtrl+Plus', role: 'zoomIn' },
{ label: '缩小', accelerator: 'CmdOrCtrl+-', role: 'zoomOut' },
{ type: 'separator' },
{ label: '切换全屏', accelerator: 'F11', role: 'togglefullscreen' }
]
},
{
label: '窗口',
submenu: [
{ label: '最小化', accelerator: 'CmdOrCtrl+M', role: 'minimize' },
{ label: '关闭', accelerator: 'CmdOrCtrl+W', role: 'close' }
]
},
{
label: '帮助',
submenu: [
{
label: '关于',
click: () => {
dialog.showMessageBox(mainWindow, {
type: 'info',
title: '关于',
message: 'Hello World',
detail: '基于 Electron 构建的应用程序'
});
}
}
]
}
];
// macOS 特殊处理
if (process.platform === 'darwin') {
menuTemplate.unshift({
label: app.getName(),
submenu: [
{ label: '关于 ' + app.getName(), role: 'about' },
{ type: 'separator' },
{ label: '服务', role: 'services', submenu: [] },
{ type: 'separator' },
{ label: '隐藏 ' + app.getName(), accelerator: 'Command+H', role: 'hide' },
{ label: '隐藏其他', accelerator: 'Command+Shift+H', role: 'hideOthers' },
{ label: '显示全部', role: 'unhide' },
{ type: 'separator' },
{ label: '退出', accelerator: 'Command+Q', click: () => app.quit() }
]
});
// 窗口菜单
menuTemplate[4].submenu = [
{ label: '关闭', accelerator: 'CmdOrCtrl+W', role: 'close' },
{ label: '最小化', accelerator: 'CmdOrCtrl+M', role: 'minimize' },
{ label: '缩放', role: 'zoom' },
{ type: 'separator' },
{ label: '前置全部窗口', role: 'front' }
];
}
const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
}
关键配置项说明
1. label(菜单项文本)
菜单项显示的文本,我们使用中文:
{ label: '文件' } // 显示为"文件"
{ label: '新建' } // 显示为"新建"
2. accelerator(快捷键)
定义菜单项的快捷键。使用 CmdOrCtrl 可以自动适配 macOS(Cmd)和 Windows/Linux(Ctrl):
{ label: '新建', accelerator: 'CmdOrCtrl+N' } // macOS: Cmd+N, Windows: Ctrl+N
常用快捷键格式:
-
CmdOrCtrl+N: 跨平台快捷键 -
Command+H: macOS 专用 -
Ctrl+Shift+R: 组合键 -
F12: 功能键
3. role(系统预定义行为)
Electron 提供了系统预定义的角色,使用 role 可以自动处理系统级功能:
{ label: '复制', role: 'copy' } // 自动实现复制功能
常用 role 值:
-
编辑类:
undo,redo,cut,copy,paste,delete,selectAll -
视图类:
reload,forceReload,toggleDevTools,resetZoom,zoomIn,zoomOut,togglefullscreen -
窗口类:
minimize,close,zoom,front -
应用类:
about,hide,hideOthers,unhide,quit
使用 role 的优势:
-
自动适配系统语言(macOS 下
copy会显示为"拷贝") -
自动绑定系统默认快捷键
-
无需编写
click事件(系统自动处理)
4. click(点击事件)
自定义菜单项的点击行为:
{
label: '新建',
click: () => {
// 向渲染进程发送消息
if (mainWindow) {
mainWindow.webContents.send('menu:new');
}
}
}
5. type(菜单项类型)
定义菜单项的类型:
{ type: 'separator' } // 分隔线
{ type: 'normal' } // 普通菜单项(默认)
{ type: 'checkbox' } // 复选框
{ type: 'radio' } // 单选框
6. submenu(子菜单)
定义子菜单项数组:
{
label: '文件',
submenu: [
{ label: '新建' },
{ label: '打开' },
// ... 更多子菜单项
]
}
跨平台适配
macOS 特殊处理
macOS 有独特的菜单栏规范,需要特殊处理:
-
应用菜单位置: macOS 的应用菜单会自动显示在系统菜单栏的第一位,标签会被替换为应用名称。
-
必需的系统菜单项: macOS 应用菜单需要包含:
-
"关于 [应用名]"
-
"服务"
-
"隐藏 [应用名]"
-
"隐藏其他"
-
"显示全部"
-
"退出"
-
-
窗口菜单: macOS 的窗口菜单需要包含"缩放"和"前置全部窗口"等选项。
实现代码:
// macOS 特殊处理
if (process.platform === 'darwin') {
// 在菜单模板最前面插入应用菜单
menuTemplate.unshift({
label: app.getName(), // 使用应用名称
submenu: [
{ label: '关于 ' + app.getName(), role: 'about' },
{ type: 'separator' },
{ label: '服务', role: 'services', submenu: [] },
{ type: 'separator' },
{ label: '隐藏 ' + app.getName(), accelerator: 'Command+H', role: 'hide' },
{ label: '隐藏其他', accelerator: 'Command+Shift+H', role: 'hideOthers' },
{ label: '显示全部', role: 'unhide' },
{ type: 'separator' },
{ label: '退出', accelerator: 'Command+Q', click: () => app.quit() }
]
});
// 修改窗口菜单
menuTemplate[4].submenu = [
{ label: '关闭', accelerator: 'CmdOrCtrl+W', role: 'close' },
{ label: '最小化', accelerator: 'CmdOrCtrl+M', role: 'minimize' },
{ label: '缩放', role: 'zoom' },
{ type: 'separator' },
{ label: '前置全部窗口', role: 'front' }
];
}
Windows/Linux 处理
Windows 和 Linux 的菜单栏显示在窗口顶部,不需要特殊的应用菜单,直接使用我们定义的中文菜单即可。
高级功能
1. 动态菜单项
根据应用状态动态显示/隐藏菜单项:
{
label: '保存',
enabled: hasUnsavedChanges, // 根据状态启用/禁用
visible: isEditorMode, // 根据状态显示/隐藏
click: () => {
saveFile();
}
}
2. 条件菜单项
根据条件显示不同的菜单项:
const fileMenu = {
label: '文件',
submenu: [
{ label: '新建', accelerator: 'CmdOrCtrl+N' },
...(isMac ? [
{ type: 'separator' },
{ label: '最近打开的文件', submenu: recentFiles }
] : [])
]
};
3. 菜单项分组
使用分隔线对菜单项进行分组:
{
label: '文件',
submenu: [
{ label: '新建' },
{ label: '打开' },
{ type: 'separator' }, // 第一组结束
{ label: '保存' },
{ label: '另存为' },
{ type: 'separator' }, // 第二组结束
{ label: '退出' }
]
}
4. 与渲染进程通信
菜单项点击时,通过 IPC 与渲染进程通信:
{
label: '新建',
click: () => {
// 向渲染进程发送消息
if (mainWindow) {
mainWindow.webContents.send('menu:new');
}
}
}
在渲染进程中监听:
// preload.js
const { ipcRenderer } = require('electron');
window.electronAPI = {
onMenuNew: (callback) => {
ipcRenderer.on('menu:new', callback);
}
};
// renderer.js
window.electronAPI.onMenuNew(() => {
// 处理新建操作
console.log('用户点击了新建菜单');
});
最佳实践
1. 菜单结构设计
-
文件菜单: 新建、打开、保存、另存为、最近文件、退出
-
编辑菜单: 撤销、重做、剪切、复制、粘贴、查找、替换
-
视图菜单: 刷新、缩放、全屏、开发者工具
-
窗口菜单: 最小化、最大化、关闭、新建窗口
-
帮助菜单: 帮助文档、关于、检查更新
2. 快捷键设计
遵循各平台的快捷键规范:
-
通用快捷键: 使用
CmdOrCtrl实现跨平台 -
平台特定: macOS 使用
Command,Windows/Linux 使用Ctrl -
功能键: F11(全屏)、F12(开发者工具)等
3. 使用 role 属性
尽可能使用 role 属性,而不是自定义 click 事件:
// ✅ 推荐:使用 role
{ label: '复制', role: 'copy' }
// ❌ 不推荐:自定义实现
{
label: '复制',
click: () => {
// 手动实现复制逻辑
}
}
4. 错误处理
在菜单项点击事件中添加错误处理:
{
label: '打开文件',
click: async () => {
try {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile']
});
if (!result.canceled) {
// 处理文件
}
} catch (error) {
console.error('打开文件失败:', error);
dialog.showErrorBox('错误', '无法打开文件');
}
}
}
5. 菜单项状态管理
根据应用状态动态更新菜单项:
function updateMenu() {
const menu = Menu.getApplicationMenu();
const fileMenu = menu.items.find(item => item.label === '文件');
const saveItem = fileMenu.submenu.items.find(item => item.label === '保存');
// 更新菜单项状态
saveItem.enabled = hasUnsavedChanges;
}
常见问题
Q1: 菜单栏不显示?
原因: Menu.setApplicationMenu() 未在 app.ready 事件后调用。
解决方案: 确保菜单创建逻辑在 app.whenReady() 内部:
app.whenReady().then(() => {
createWindow();
createApplicationMenu(); // ✅ 正确位置
});
Q2: macOS 菜单显示为英文?
原因: 使用了 role 属性,系统会自动使用系统语言。
解决方案: 如果希望完全控制菜单文本,不使用 role,而是自定义 click 事件:
// 使用中文标签,自定义实现
{
label: '复制',
accelerator: 'CmdOrCtrl+C',
click: () => {
// 自定义复制逻辑
}
}
Q3: 快捷键不生效?
原因: 快捷键格式错误或与其他应用冲突。
解决方案:
-
检查快捷键格式是否正确
-
使用
CmdOrCtrl而不是Cmd或Ctrl -
避免使用系统保留的快捷键
Q4: 如何禁用某个菜单项?
使用 enabled 属性:
{
label: '保存',
enabled: false, // 禁用菜单项
click: () => {}
}
Q5: 如何隐藏某个菜单项?
使用 visible 属性:
{
label: '高级功能',
visible: isAdvancedMode, // 根据条件显示/隐藏
click: () => {}
}
总结
本文详细介绍了如何在 Electron 应用中实现完整的中文菜单栏,包括:
-
基础实现: 创建菜单模板、设置菜单栏
-
跨平台适配: macOS 特殊处理、Windows/Linux /HarmonyOS PC适配
-
高级功能: 动态菜单、条件显示、IPC 通信
-
最佳实践: 菜单结构设计、快捷键规范、错误处理
-
问题解决: 常见问题及解决方案
通过本文的指导,你可以为 Electron 应用创建一个完整、专业、用户友好的中文菜单栏,提升用户体验和应用的专业度。
完整代码示例
完整的实现代码已包含在本文中,你可以直接复制使用。记住:
-
在
app.whenReady()中调用createApplicationMenu() -
根据实际需求调整菜单项和功能
-
测试不同平台下的菜单显示效果
-
遵循各平台的菜单设计规范
希望本文能帮助你成功实现 Electron 中文菜单栏!
更多推荐


所有评论(0)