Electron.js如何在渲染进程中处理全局快捷键与文件菜单点击事件
解决方案
前置说明
Electron 主进程向渲染进程通信核心依赖 webContents.send() 方法,结合上下文隔离环境下的 preload 脚本暴露 IPC 接口即可实现,以下是适配 electron-forge + TypeScript 环境的具体实现:
步骤1:配置 preload 脚本(核心)
electron-forge 默认开启上下文隔离,你需要先在 preload.ts 中通过 contextBridge 暴露跨进程通信接口:
// preload.ts import { contextBridge, ipcRenderer } from 'electron'; contextBridge.exposeInMainWorld('electronAPI', { // 监听主进程发来的事件 on: (channel: string, callback: (...args: any[]) => void) => { ipcRenderer.on(channel, (_, ...args) => callback(...args)); }, // 向主进程发送消息 send: (channel: string, ...args: any[]) => { ipcRenderer.send(channel, ...args); } });
同时新增类型声明文件 global.d.ts 避免 TS 类型报错:
// global.d.ts export {}; declare global { interface Window { electronAPI: { on: (channel: string, callback: (...args: any[]) => void) => void; send: (channel: string, ...args: any[]) => void; }; } }
创建窗口时记得在 webPreferences 中配置 preload 脚本路径。
步骤2:主进程侧发送消息逻辑
首先需要存储你创建的 BrowserWindow 实例,后续通过该实例的 webContents.send() 方法向渲染进程发消息:
2.1 全局快捷键触发时发消息
// index.ts 主进程 import { app, BrowserWindow, globalShortcut, Menu, dialog, ipcMain } from 'electron'; import * as fs from 'fs'; import * as path from 'path'; let mainWindow: BrowserWindow | null; // 你的窗口创建逻辑 function createWindow() { mainWindow = new BrowserWindow({ webPreferences: { preload: path.join(__dirname, 'preload.js'), // 替换为你的preload实际路径 contextIsolation: true, nodeIntegration: false } }); mainWindow.loadFile('index.html'); }
在全局快捷键注册逻辑中调用发送方法:
app.whenReady().then(() => { // 示例:注册全局保存快捷键 globalShortcut.register('CommandOrControl+S', () => { // 向渲染进程发送快捷键触发事件,第二个参数为自定义参数 mainWindow?.webContents.send('global-shortcut-trigger', 'save'); }); });
2.2 File菜单触发时发消息
在菜单模板的点击回调中发送对应事件:
const menuTemplate = [ { label: 'File', submenu: [ { label: 'Save', accelerator: 'CommandOrControl+S', click: () => mainWindow?.webContents.send('file-save-request') }, { label: 'Save As', click: () => mainWindow?.webContents.send('file-save-as-request') }, { label: 'Load File', click: () => mainWindow?.webContents.send('file-load-request') } ] } ]; Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate));
同时补充主进程侧的文件读写逻辑,监听渲染进程发来的文件操作请求:
// 监听保存请求 ipcMain.on('save-file', async (_, content: string, isSaveAs: boolean) => { if (!mainWindow) return; const filePath = isSaveAs ? (await dialog.showSaveDialog(mainWindow)).filePath : // 这里可以存储上次保存的路径复用,逻辑自行补充 (await dialog.showSaveDialog(mainWindow)).filePath; if (filePath) { fs.writeFileSync(filePath, content, 'utf-8'); } }); // 监听加载请求 ipcMain.on('load-file', async () => { if (!mainWindow) return; const { filePaths } = await dialog.showOpenDialog(mainWindow, { properties: ['openFile'], filters: [{ name: 'Text Files', extensions: ['txt', 'md'] }] }); if (filePaths?.length) { const content = fs.readFileSync(filePaths[0], 'utf-8'); // 读取完成后把内容发给渲染进程 mainWindow.webContents.send('file-content-loaded', content); } });
步骤3:渲染进程侧监听事件处理DOM
在 renderer.ts 中监听主进程发来的事件,直接操作 textarea 元素即可:
// renderer.ts const textarea = document.querySelector('textarea') as HTMLTextAreaElement; // 监听全局快捷键触发 window.electronAPI.on('global-shortcut-trigger', (action: string) => { if (action === 'save') { // 拿到textarea内容发给主进程保存 window.electronAPI.send('save-file', textarea.value, false); } }); // 监听菜单Save请求 window.electronAPI.on('file-save-request', () => { window.electronAPI.send('save-file', textarea.value, false); }); // 监听菜单Save As请求 window.electronAPI.on('file-save-as-request', () => { window.electronAPI.send('save-file', textarea.value, true); }); // 监听菜单Load请求 window.electronAPI.on('file-load-request', () => { window.electronAPI.send('load-file'); }); // 监听主进程返回的加载文件内容 window.electronAPI.on('file-content-loaded', (content: string) => { textarea.value = content; });
内容的提问来源于stack exchange,提问作者pokemon
相关产品推荐
相关产品推荐

