Electron进程间通信IPC
大约 2 分钟
Electron 进程间通信(IPC)
通信模型
渲染进程出于安全被沙箱隔离,不能直接调用系统 API。所有系统能力(读写文件、弹窗、窗口控制等)都要:
渲染进程 → preload(contextBridge 暴露接口)→ ipcRenderer → 主进程 ipcMain 处理 → 返回结果
模式一:invoke / handle(推荐,请求-响应)
主进程 main.js
const { app, BrowserWindow, ipcMain, dialog } = require("electron");
const path = require("path");
const fs = require("fs");
ipcMain.handle("dialog:openFile", async () => {
const { canceled, filePaths } = await dialog.showOpenDialog();
if (canceled) return null;
return filePaths[0];
});
// 读取文件内容返回给渲染进程
ipcMain.handle("file:read", async (event, filePath) => {
return fs.promises.readFile(filePath, "utf-8");
});
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js")
}
});
win.loadFile("index.html");
};
app.whenReady().then(createWindow);
预加载脚本 preload.js
const { contextBridge, ipcRenderer } = require("electron");
// 只暴露白名单通道,不要直接把 ipcRenderer 整个暴露出去!
contextBridge.exposeInMainWorld("electronAPI", {
openFile: () => ipcRenderer.invoke("dialog:openFile"),
readFile: filePath => ipcRenderer.invoke("file:read", filePath)
});
渲染进程(页面中)
// 页面 JS 中直接使用 window.electronAPI
const btn = document.getElementById("openBtn");
btn.addEventListener("click", async () => {
const filePath = await window.electronAPI.openFile();
if (filePath) {
const content = await window.electronAPI.readFile(filePath);
console.log(content);
}
});
模式二:send / on(单向消息,适合通知)
渲染进程 → 主进程
// preload.js
contextBridge.exposeInMainWorld("electronAPI", {
sendMessage: message => ipcRenderer.send("message:send", message)
});
// 页面
window.electronAPI.sendMessage("hello main");
// main.js
ipcMain.on("message:send", (event, message) => {
console.log("收到渲染进程消息:", message);
// event.sender 就是谁发来的 webContents,可以直接回复
event.sender.send("message:reply", "received: " + message);
});
主进程 → 渲染进程(主动推送)
// 主进程:向指定窗口推送消息(如下载进度)
win.webContents.send("download:progress", 45);
// preload.js:暴露订阅接口
contextBridge.exposeInMainWorld("electronAPI", {
onDownloadProgress: callback =>
ipcRenderer.on("download:progress", (event, percent) => callback(percent))
});
// 页面
window.electronAPI.onDownloadProgress(percent => {
console.log("下载进度:", percent);
});
Demo:渲染进程控制窗口
main.js
ipcMain.on("window:minimize", event => {
BrowserWindow.fromWebContents(event.sender)?.minimize();
});
ipcMain.on("window:maximize", event => {
const win = BrowserWindow.fromWebContents(event.sender);
win?.isMaximized() ? win.unmaximize() : win?.maximize();
});
ipcMain.on("window:close", event => {
BrowserWindow.fromWebContents(event.sender)?.close();
});
preload.js
contextBridge.exposeInMainWorld("electronAPI", {
minimize: () => ipcRenderer.send("window:minimize"),
maximize: () => ipcRenderer.send("window:maximize"),
close: () => ipcRenderer.send("window:close")
});
自定义标题栏按钮(页面)
<div class="titlebar">
<button onclick="window.electronAPI.minimize()">—</button>
<button onclick="window.electronAPI.maximize()">□</button>
<button onclick="window.electronAPI.close()">×</button>
</div>
安全清单
contextIsolation保持开启(默认),禁止nodeIntegration: true- preload 中白名单式暴露方法,不要
exposeInMainWorld("ipc", ipcRenderer) - ipcMain 校验来自渲染进程的参数(渲染进程可能被 XSS 注入)
win.loadURL只加载可信页面,will-navigate事件中拦截外部跳转- 新版 Electron 建议开启
sandbox: true
Loading...
