IPCMain 介绍:
IPCMain 从主进程到渲染进程的异步通信,要想与渲染进程通信,则需通过preload配置通信桥。
IpcRenderer 从渲染进程到主进程的异步通信。
通信桥:
contextBridge.exposeInMainWorld(
'electron': {
ipcRenderer: {
...ipcMain.API
}
}
)
IpcMainEvent 参数总结:
-
type 字符串
-
可能的值包括frame
事件类型
// 示例值
event.type = "frame";
// 可能的其他值:根据实际应用可能还有 "window", "dialog" 等
processId Integer
-
发送该消息的渲染进程内部的ID
渲染进程 ID
// 示例值
event.processId = 12345;
// 这标识了发送消息的渲染进程的唯一ID
frameId Integer
-
发送该消息的渲染进程框架的ID(可能是iframe)
渲染进程框架 ID // 示例值 event.frameId = 1; // 主框架 // 或 event.frameId = 2; // iframe框架
returnValue any
-
如果对此赋值,则该值会在同步消息中返回
同步消息返回值
// 示例:在同步IPC消息中设置返回值 event.returnValue = "操作成功"; // 或 event.returnValue = { data: "返回的数据", status: "success" }; 示例: 主进程: ipcMain.on('channel-name', (event, data) => { event.returnValue = '消息' }) 渲染进程:必须要用sendSync才能获取到内容 const getValue = ipcRenderer.sendSync('channel-name') console.log(getValue) // 消息
sender WebContents
-
返回webContents 发送消息的内容
发送消息的WebContents
sender.webContents = BrowserWindow.webContents 示例(异步): // 主进程 ipcMain.on('channel-name', (event: Electron.IpcMainEvent, data) => { console.log('收到消息:', data) event.sender.send('response-channel', { status: 'success', message: '处理完成', result: data * 2 }) }) // 渲染进程 ipcRenderer.send( 'channel-name', 50 ) ipcRenderer.on('response-channel', (getValue: any) => { console.log(getValue) 结果:{ "status": "success", "message": "处理完成", "result": 100 } })
senderFrame WebFrameMain | null只读
-
发送此消息的帧。null 如果在帧已导航或已销毁后访问此消息,则可能为空。
发送消息的框架
// 示例:检查发送框架 if (event.senderFrame) { console.log(event.senderFrame.url); // 框架的URL } else { console.log("框架已销毁或不存在"); }
ports MessagePortMain []
- 此消息传输的消息端口列表
使用场景:
MessagePort 是需要"专线高速公路"时才会用的。普通路(IPC)不够用时再用它
官方推荐使用MessagePort的典型场景:
-
高吞吐消息
-
长连接
-
设备驱动
-
日志流
-
WebRTC媒体包通信
-
持续大量事件流
若是高频业务(如:蓝牙流、TCP流、日志)必须要独立一个MessageChannelMain()// 主进程
const chBLE = new MessageChannelMain();const chTCP = new MessageChannelMain(); const chLog = new MessageChannelMain();// preload
ipcRenderer.on('port-ble', (e) => window.postMessage('port-ble', '', e.ports));
ipcRenderer.on('port-tcp', (e) => window.postMessage('port-tcp', '', e.ports));
ipcRenderer.on('port-log', (e) => window.postMessage('port-log', '*', e.ports));// channel
window.addEventListener("message", (e) => {
if (e.data === "port-ble") setupBLE(e.ports[0]);
if (e.data === "port-tcp") setupTCP(e.ports[0]);
});
实践代码示例:
后面也有找到一个使用MeesageChannelMain的另一个示例:
https://gist.github.com/johannesgiani/a27fab2121dadb1b37820e891dac0aaf
# 传输的MessagePorts
## 主进程
// 先前一直使用once,导致渲染进程的热更新,就会丢失通信,所以改为on,
mainWindow.webContents.on('did-finish-load', () => {
const { port1, port2 } = new MessageChannelMain();
port2.postMessage({ test: 123 })
port2.on('message', (event) => {
console.log('main port2 on message', event.data);
// 收到渲染进程消息后,再回一条
port2.postMessage({ from: 'main', echo: event.data });
});
port2.start();
console.log('[main] posting port1 to renderer');
// 让主进程稍微延迟发送 port,用于测试功能
setTimeout(() => {
mainWindow.webContents.postMessage('main-message', { hello: 'world' }, [port1]);
}, 500); // 等 React mount
});
## preload.js配置
可以配置在preload.js中的contextBridge.exposeInMainWorld的顶部,但绝对不能在contextBridge.exposeInMainWorld内部配置,这个和渲染进程的contextBridge不冲突,但需要各自配置各自的
const windowLoaded = new Promise(resolve => {
window.onload = resolve
})
// 监听来自主进程的 webContents.postMessage
ipcRenderer.on('main-message', async (event: any) => {
console.log('[preload] got main-message, forwarding port to window.main world', event.data, 'ports:', event.ports);
await windowLoaded;
// 把 port 转发给页面主世界
// 注意 event.ports 是一个可传递的 MessagePortMain 实例数组
window.postMessage('main-message', '*', event.ports);
});
## 渲染进程
// 设置 window.onmessage 接受来自 preload 转发的 port
window.onmessage = (event) => {
console.log('[renderer] actual event.data =', event.data);
// 只处理预期的 channel 名称
if (event.source !== window || event.data !== 'main-message') {
// 可能是别的来源(iframe、devtools 等),忽略
return;
}
console.log('[renderer] received postMessage from preload, data=', event.data, 'ports=', event.ports);
const port = event.ports && event.ports[0];
if (!port) {
console.log('[renderer] No port received!');
return;
}
// 给 port 注册 onmessage
port.onmessage = (e) => {
console.log('[renderer] port.onmessage:', e.data);
};
// 向主进程发一条消息(通过 port)
port.postMessage({ from: 'renderer', note: 'hello main! (via port)', ts: Date.now() });
// 测试:再发送一个响应来模拟业务
setTimeout(() => {
port.postMessage({ from: 'renderer', note: 'delayed message', ts: Date.now() });
}, 500);
};
reply Function
将 IPC 消息发送到渲染器框架的函数,该渲染器框架发送当前正在处理的原始消息。 您应该使用"reply"方法回复发送的消息,以确保回复将转到正确的进程和框架。
channel 细绳
...args 任何[]
#异步消息处理
## 主进程
ipcMain.on('channel-name', (event, data) => {
console.log('收到消息:', data)
// 使用 reply 方法回复
event.reply('response-channel', {
status: 'success',
message: '处理完成',
result: data * 2
})
})
## 渲染进程
// 异步消息
ipcRenderer.send('channel-name', 5)
ipcRenderer.on('response-channel', (response) => { // 注意参数,我这里的preload配置没有传送回来event
console.log('收到回复:', response) // {status: 'success', message: '处理完成', result: 10}
})
#同步消息处理
## 主进程
ipcMain.on('sync-calculation', (event, data) => {
console.log('同步计算请求:', data)
// 对于同步消息,设置 returnValue
if (data.operation === 'add') {
event.returnValue = data.a + data.b
} else if (data.operation === 'multiply') {
event.returnValue = data.a * data.b
} else {
event.returnValue = null
}
})
## 渲染进程
// 同步消息
const result = ipcRenderer.sendSync('sync-calculation', {
operation: 'add',
a: 10,
b: 20
})
console.log('同步结果:', result) // 30