IPC 通信
通信架构

Preview
IPC Manager
管理器架构
IPC Manager 负责统一管理所有 IPC 处理器的注册、生命周期和错误处理:
javascript
class IpcManager {
// Handler 配置表
static HANDLERS = [
{
name: 'NativeFocus',
handler: NativeFocusIpcHandler,
priority: 1, // 最高优先级
timeout: 2000, // 超时时间(毫秒)
required: true, // 是否必需
},
{
name: 'FloatingBall',
handler: FloatingBallIpcHandler,
priority: 1,
timeout: 2000,
required: false,
},
{
name: 'Print',
handler: PrintIpcHandler,
priority: 2,
timeout: 3000,
required: false,
},
{
name: 'Plugin',
handler: PluginIpcHandler,
priority: 2,
timeout: 5000,
required: false,
},
{
name: 'Machine',
handler: MachineIpcHandler,
priority: 2,
timeout: 15000,
required: false,
},
];
}配置项说明
| 配置项 | 说明 | 示例 |
|---|---|---|
name | Handler 名称 | 'Machine' |
handler | Handler 类 | MachineIpcHandler |
priority | 优先级(1最高) | 1, 2 |
timeout | 注册超时时间(ms) | 2000 |
required | 是否必需(失败时终止启动) | true, false |
注册流程
javascript
/**
* 注册所有 IPC 处理器(按优先级分组并行)
*/
static async registerHandlers() {
const startTime = Date.now();
try {
// 按优先级分组
const priorityGroups = this.HANDLERS.reduce((groups, handler) => {
const priority = handler.priority;
if (!groups[priority]) groups[priority] = [];
groups[priority].push(handler);
return groups;
}, {});
// 按优先级顺序执行,同一优先级并行注册
const allResults = [];
for (const priority of Object.keys(priorityGroups).sort()) {
const handlers = priorityGroups[priority];
logger.info(`注册优先级 ${priority} 的 ${handlers.length} 个 Handler (并行)...`);
// 同一优先级的 Handler 并行注册
const results = await Promise.allSettled(
handlers.map((config) => this.registerHandler(config))
);
// 提取结果
const groupResults = results.map((result, index) => {
if (result.status === 'fulfilled') {
return result.value;
} else {
const config = handlers[index];
return {
success: false,
name: config.name,
error: result.reason.message,
required: config.required,
};
}
});
allResults.push(...groupResults);
// 检查是否有必需的 Handler 失败
const criticalFailure = groupResults.find((r) => !r.success && r.required);
if (criticalFailure) {
logger.error(`关键 Handler [${criticalFailure.name}] 注册失败,终止启动`);
await this.removeHandlers();
throw new Error(`关键 IPC Handler 注册失败: ${criticalFailure.error}`);
}
}
// 汇总统计
const successCount = allResults.filter((r) => r.success).length;
const failedCount = allResults.filter((r) => !r.success).length;
const elapsed = Date.now() - startTime;
logger.info(
`IPC Handler 注册完成: 成功 ${successCount}/${this.HANDLERS.length}, ` +
`失败 ${failedCount}, 耗时 ${elapsed}ms`
);
// 记录失败的非必需 Handler
allResults
.filter((r) => !r.success)
.forEach((r) => logger.warn(`非关键 Handler [${r.name}] 已降级运行: ${r.error}`));
} catch (error) {
logger.error('IPC 处理器注册失败:', error);
await this.removeHandlers();
throw error;
}
}超时保护
javascript
/**
* 超时包装器
*/
static withTimeout(promise, ms, name) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`${name} 注册超时 (${ms}ms)`)), ms)
)
]);
}错误隔离
javascript
/**
* 注册单个 Handler(带错误隔离)
*/
static async registerHandler(config) {
const { name, handler, timeout, required } = config;
const startTime = Date.now();
try {
logger.info(`开始注册 ${name} Handler...`);
// 带超时保护的注册
await this.withTimeout(handler.register(), timeout, name);
// 记录成功状态
this.registeredHandlers.add(name);
const elapsed = Date.now() - startTime;
logger.info(`${name} Handler 注册成功 (${elapsed}ms)`);
return { success: true, name };
} catch (error) {
const elapsed = Date.now() - startTime;
logger.error(`${name} Handler 注册失败 (${elapsed}ms):`, error);
// 必需的 Handler 失败,向上抛出错误
if (required) {
throw new Error(`关键 Handler [${name}] 注册失败: ${error.message}`);
}
// 非必需的失败,记录但不中断流程
return { success: false, name, error: error.message };
}
}Handler 实现规范
基本结构
每个 IPC Handler 应该实现以下方法:
javascript
class ExampleIpcHandler {
/**
* 注册 IPC 处理器
*/
static async register() {
// 注册 IPC 监听器
ipcMain.handle('example-channel', this.handleExample);
ipcMain.on('example-event', this.handleEvent);
}
/**
* 移除 IPC 处理器
*/
static removeHandlers() {
// 移除所有监听器
ipcMain.removeHandler('example-channel');
ipcMain.removeListener('example-event', this.handleEvent);
}
/**
* 处理 IPC 请求
*/
static async handleExample(event, data) {
try {
// 处理逻辑
return { success: true, data: result };
} catch (error) {
logger.error('处理失败:', error);
return { success: false, error: error.message };
}
}
/**
* 处理 IPC 事件
*/
static handleEvent(event, data) {
// 事件处理逻辑
}
}IPC 通信模式
1. 单向通信(Renderer → Main)
javascript
// 主进程
ipcMain.on('channel-name', (event, data) => {
console.log('收到消息:', data);
});
// 渲染进程(通过 preload)
ipcRenderer.send('channel-name', { message: 'Hello' });2. 请求-响应(Renderer → Main → Renderer)
javascript
// 主进程
ipcMain.handle('channel-name', async (event, data) => {
const result = await processData(data);
return result;
});
// 渲染进程(通过 preload)
const result = await ipcRenderer.invoke('channel-name', { query: 'data' });3. 主进程推送(Main → Renderer)
javascript
// 主进程
win.webContents.send('channel-name', { message: 'Update' });
// 渲染进程(通过 preload)
ipcRenderer.on('channel-name', (event, data) => {
console.log('收到推送:', data);
});4. 双向通信
javascript
// 主进程发送并等待响应
win.webContents.send('request-channel', { data });
ipcMain.once('response-channel', (event, response) => {
console.log('收到响应:', response);
});
// 渲染进程
ipcRenderer.on('request-channel', (event, data) => {
// 处理并响应
ipcRenderer.send('response-channel', { result });
});Preload 脚本
安全的 API 暴露
javascript
// packages/main/src/preload/preload.js
const { contextBridge, ipcRenderer } = require('electron');
// 暴露安全的 API 到渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
// 发送消息到主进程
send: (channel, data) => {
const validChannels = ['channel-1', 'channel-2'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
// 调用主进程方法并获取返回值
invoke: async (channel, data) => {
const validChannels = ['get-data', 'process-data'];
if (validChannels.includes(channel)) {
return await ipcRenderer.invoke(channel, data);
}
},
// 监听主进程消息
on: (channel, callback) => {
const validChannels = ['update-status', 'notification'];
if (validChannels.includes(channel)) {
const subscription = (event, ...args) => callback(...args);
ipcRenderer.on(channel, subscription);
// 返回取消订阅函数
return () => {
ipcRenderer.removeListener(channel, subscription);
};
}
},
// 监听一次
once: (channel, callback) => {
const validChannels = ['init-complete'];
if (validChannels.includes(channel)) {
ipcRenderer.once(channel, (event, ...args) => callback(...args));
}
},
});渲染进程使用
javascript
// 在网页中使用
// 发送消息
window.electronAPI.send('channel-1', { data: 'value' });
// 调用方法
const result = await window.electronAPI.invoke('get-data', { id: 123 });
// 监听消息
const unsubscribe = window.electronAPI.on('update-status', (status) => {
console.log('状态更新:', status);
});
// 取消监听
unsubscribe();内置 IPC Handlers
1. MachineIpcHandler - 机器信息
javascript
// 主进程
ipcMain.handle('get-machine-info', async () => {
return MachineService.getMachineInfoData();
});
// 渲染进程
const machineInfo = await window.electronAPI.invoke('get-machine-info');
console.log('机器信息:', machineInfo);2. NativeFocusIpcHandler - 焦点处理
javascript
// 主进程
ipcMain.on('set-native-focus', (event, options) => {
NativeFocusHandler.setFocus(options);
});
// 渲染进程
window.electronAPI.send('set-native-focus', { target: 'input' });3. PrintIpcHandler - 打印功能
javascript
// 主进程
ipcMain.handle('print-page', async (event, options) => {
return await PrintService.print(options);
});
// 渲染进程
const result = await window.electronAPI.invoke('print-page', {
silent: false,
printBackground: true,
});4. PluginIpcHandler - 插件管理
javascript
// 主进程
ipcMain.handle('plugin-list', async () => {
return await PluginLoader.getPluginList();
});
ipcMain.handle('plugin-start', async (event, pluginId) => {
return await PluginLoader.startPlugin(pluginId);
});
// 渲染进程
const plugins = await window.electronAPI.invoke('plugin-list');
await window.electronAPI.invoke('plugin-start', 'plugin-id');5. FloatingBallIpcHandler - 悬浮球
javascript
// 主进程
ipcMain.handle('show-floating-ball', async () => {
FloatingBallService.showFloatingBall();
});
ipcMain.handle('hide-floating-ball', async () => {
FloatingBallService.hideFloatingBall();
});
// 渲染进程
await window.electronAPI.invoke('show-floating-ball');
await window.electronAPI.invoke('hide-floating-ball');错误处理
统一错误响应格式
javascript
// 成功响应
{
success: true,
data: { ... }
}
// 错误响应
{
success: false,
error: 'Error message',
code: 'ERROR_CODE'
}Handler 中的错误处理
javascript
static async handleRequest(event, data) {
try {
const result = await processData(data);
return { success: true, data: result };
} catch (error) {
logger.error('处理请求失败:', error);
return {
success: false,
error: error.message,
code: error.code || 'UNKNOWN_ERROR'
};
}
}渲染进程中的错误处理
javascript
try {
const result = await window.electronAPI.invoke('channel-name', data);
if (!result.success) {
throw new Error(result.error);
}
// 处理成功结果
console.log(result.data);
} catch (error) {
console.error('调用失败:', error);
// 显示错误提示
}性能优化
1. 避免频繁 IPC 调用
javascript
// ❌ 不推荐:频繁调用
setInterval(() => {
window.electronAPI.invoke('get-status');
}, 100);
// ✅ 推荐:使用事件推送
window.electronAPI.on('status-update', (status) => {
console.log('状态:', status);
});
// 主进程定时推送
setInterval(() => {
win.webContents.send('status-update', getStatus());
}, 1000);2. 批量处理
javascript
// ❌ 不推荐:逐个处理
for (const item of items) {
await window.electronAPI.invoke('process-item', item);
}
// ✅ 推荐:批量处理
await window.electronAPI.invoke('process-items', items);3. 数据缓存
javascript
// 缓存机器信息,避免重复获取
let machineInfoCache = null;
async function getMachineInfo() {
if (!machineInfoCache) {
machineInfoCache = await window.electronAPI.invoke('get-machine-info');
}
return machineInfoCache;
}4. 使用 MessagePort(大数据传输)
javascript
// 主进程
ipcMain.on('create-port', (event) => {
const { port1, port2 } = new MessageChannelMain();
event.sender.postMessage('port', null, [port2]);
port1.on('message', (event) => {
// 处理大数据
});
});
// 渲染进程
ipcRenderer.once('port', (event) => {
const port = event.ports[0];
port.postMessage(largeData);
});安全最佳实践
1. 白名单验证
javascript
// Preload 中验证频道白名单
contextBridge.exposeInMainWorld('electronAPI', {
invoke: async (channel, data) => {
const validChannels = ['get-data', 'save-data'];
if (!validChannels.includes(channel)) {
throw new Error(`Invalid channel: ${channel}`);
}
return await ipcRenderer.invoke(channel, data);
},
});2. 参数验证
javascript
// 主进程中验证参数
ipcMain.handle('save-data', async (event, data) => {
// 验证数据结构
if (!data || typeof data.id !== 'number') {
throw new Error('Invalid data format');
}
// 清理危险字符
const sanitized = sanitizeInput(data);
return await saveData(sanitized);
});3. 权限检查
javascript
// 检查来源窗口权限
ipcMain.handle('admin-action', async (event, data) => {
const sender = BrowserWindow.fromWebContents(event.sender);
if (!isAuthorizedWindow(sender)) {
throw new Error('Unauthorized');
}
return await performAdminAction(data);
});4. 限流
javascript
// 防止频繁调用
const rateLimiter = new Map();
ipcMain.handle('expensive-operation', async (event, data) => {
const senderId = event.sender.id;
const lastCall = rateLimiter.get(senderId) || 0;
const now = Date.now();
if (now - lastCall < 1000) {
throw new Error('Rate limit exceeded');
}
rateLimiter.set(senderId, now);
return await expensiveOperation(data);
});健康检查
javascript
/**
* 获取 IPC 系统健康状态
*/
static getHealthStatus() {
const total = this.HANDLERS.length;
const registered = this.registeredHandlers.size;
const missing = this.HANDLERS
.filter((h) => !this.registeredHandlers.has(h.name))
.map((h) => h.name);
return {
healthy: registered === total,
registered,
total,
missingHandlers: missing,
};
}
// 使用示例
const health = IpcManager.getHealthStatus();
console.log('IPC 系统健康:', health);
// { healthy: true, registered: 5, total: 5, missingHandlers: [] }相关文件
packages/main/src/apps/app-ipc-handler.js- IPC 管理器packages/main/src/ipc-handlers/- 各个 IPC 处理器packages/main/src/preload/preload.js- 预加载脚本