Electron - 系统托盘
Electron - 系统托盘
Section titled “Electron - 系统托盘”系统托盘图标(在 Windows 上称为通知区域图标,在 macOS 上称为菜单栏图标)为应用程序提供了一种持续访问和状态指示的方式,即使主应用程序窗口已关闭或隐藏。Electron 允许你创建和管理这些图标及其关联的上下文菜单(Context Menu)。
重要提示:系统托盘图标及其菜单应在主进程(Main Process)中管理,而不是在渲染进程(Renderer Process)中。不鼓励使用(现已废弃且不安全)remote 模块尝试从渲染进程管理它们。
让我们创建一个带有上下文菜单的简单系统托盘图标。你需要一个图标图片(例如,一个 16x16 或 32x32 的 PNG 文件)。对于此示例,假设你在相对于 main.js 文件的 assets 子文件夹中有一个 icon.png 文件。
修改你的 main.js 文件:
const { app, BrowserWindow, Tray, Menu, nativeImage } = require('electron');const path = require('path');
let mainWindow;let tray = null; // Keep a reference to the Tray object
function createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { // Preload script can be added if renderer needs to communicate about tray actions // preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, } });
mainWindow.loadFile(path.join(__dirname, 'index.html'));
mainWindow.on('closed', () => { mainWindow = null; });}
function createTray() { // It's recommended to use nativeImage.createFromPath for icons. // Ensure the path is correct and the icon exists. // For macOS, template images are often preferred (black and white, adapting to theme). const iconPath = path.join(__dirname, 'assets', 'icon.png'); // Adjust path as needed const icon = nativeImage.createFromPath(iconPath);
if (icon.isEmpty()) { console.error('Failed to load tray icon. Is the path correct and image valid?'); // Fallback or error handling tray = new Tray(nativeImage.createEmpty()); // Creates a blank space, better than crashing } else { tray = new Tray(icon); }
const contextMenu = Menu.buildFromTemplate([ { label: 'Show App', click: () => { if (mainWindow) { mainWindow.show(); } else { createWindow(); } } }, { label: 'Item 1', click: () => { console.log('Clicked Item 1'); // If you need to notify the renderer, use IPC: // if (mainWindow) mainWindow.webContents.send('tray-action', 'item1-clicked'); } }, { label: 'Quit', click: () => { app.quit(); // Quits the application } } ]);
tray.setToolTip('My Electron App'); tray.setContextMenu(contextMenu);
// Optional: Handle left-click on tray icon (e.g., toggle window visibility) tray.on('click', () => { if (mainWindow) { if (mainWindow.isVisible()) { mainWindow.hide(); } else { mainWindow.show(); } } });}
app.whenReady().then(() => { createWindow(); createTray();
app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } });});
// Prevent app from quitting when all windows are closed if tray icon is present (common behavior)// This depends on your desired UX.app.on('window-all-closed', () => { // On macOS, it's common for applications to stay active until the user quits explicitly (Cmd+Q) // On Windows/Linux, if you want the app to close, remove this or app.quit() if (process.platform === 'darwin') { // Do nothing, app stays open with tray icon } else { // If you want the app to quit, then app.quit(). // If you want it to stay alive (tray only), then do nothing here. // For this example, let's assume we want it to stay active if tray is present. }});
// Ensure tray is destroyed when app quits to prevent zombie icons on some platformsapp.on('before-quit', () => { if (tray) { tray.destroy(); }});创建一个简单的 index.html 文件。它的内容对于此托盘示例并不重要,因为所有逻辑都在 main.js 中进行:
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>Electron Tray App</title></head><body> <h1>My Electron Application</h1> <p>Check your system tray for the application icon!</p></body></html>确保你在 assets 文件夹(例如,your-app-root/assets/icon.png)中有一个 icon.png(或你选择的图标文件)。
使用 npm start 运行应用(假设你的 package.json 已配置好)。
你应该会看到你的应用窗口和系统托盘中的图标。右键单击托盘图标会显示你定义的上下文菜单。点击“显示应用”会聚焦或重新创建窗口,“退出”会退出应用。
最佳实践和注意事项:
Section titled “最佳实践和注意事项:”- 图标路径(Icon Paths): 使用
path.join(__dirname, ...)获取可靠的assets路径。nativeImage.createFromPath()是加载图标的推荐方式。 - 图标尺寸和类型(Icon Size and Type): 托盘图标很小。通常是 16x16 或 32x32 像素。PNG 是一种常见的格式。macOS 支持“模板图像”(template images),它会根据系统主题调整(通常是黑白)。
- 主进程逻辑(Main Process Logic): 所有
Tray和Menu的创建和管理必须在主进程中进行。 - 用于渲染进程交互的 IPC(IPC for Renderer Interaction): 如果一个托盘菜单操作需要影响渲染进程(例如,打开特定视图),使用 IPC(进程间通信,通过
mainWindow.webContents.send()等)从主进程向渲染进程通信。 - 生命周期管理(Lifecycle Management): 保留对
Tray对象的引用(tray = new Tray(...)),以防止它被过早地垃圾回收。在应用的before-quit事件中显式销毁托盘图标(tray.destroy())以清理资源,尤其是在 Linux 上。 - 平台差异(Platform Differences): 托盘图标的行为和外观在 macOS、Windows 和 Linux 上可能略有差异。在所有目标平台上进行测试。
对于更高级的托盘功能,例如动态更新图标或处理不同的点击事件,请参考 Electron Tray 文档。