Реалізація міжпроцесного взаємодії (IPC) в Electron-приложенні
IPC в Electron — це єдиний безпечний спосіб дати renderer-процесу доступ до системних ресурсів. Renderer працює в ізольованому Chromium-контексті; всі з файловою системою, нативними API та Node.js — лише через IPC та preload script.
Архітектура безпечного IPC
Renderer (Chromium)
↓ window.electronAPI.someMethod()
Preload Script (contextBridge)
↓ ipcRenderer.invoke('channel', payload)
Main Process (Node.js)
↓ ipcMain.handle('channel', handler)
Preload — границя безпеки. Він експонує в renderer ровно те API, яке потрібно, та нічого лишнього.
Preload: типізований мост
// main/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
type FileInfo = { path: string; content: string; size: number };
const api = {
fs: {
readFile: (path: string): Promise<FileInfo> =>
ipcRenderer.invoke('fs:readFile', path),
writeFile: (path: string, content: string): Promise<void> =>
ipcRenderer.invoke('fs:writeFile', path, content),
},
app: {
getVersion: (): Promise<string> =>
ipcRenderer.invoke('app:getVersion'),
},
window: {
minimize: () => ipcRenderer.send('window:minimize'),
maximize: () => ipcRenderer.send('window:maximize'),
close: () => ipcRenderer.send('window:close'),
}
};
contextBridge.exposeInMainWorld('electronAPI', api);
export type ElectronAPI = typeof api;
Main: обробники IPC
// main/ipc-handlers.js
const { ipcMain, app, dialog } = require('electron');
const fs = require('fs/promises');
const path = require('path');
function registerHandlers() {
// handle — для invoke (з відповіддю)
ipcMain.handle('fs:readFile', async (event, filePath) => {
const resolvedPath = path.resolve(filePath);
const allowedDirs = [
app.getPath('documents'),
app.getPath('downloads'),
app.getPath('userData')
];
const isAllowed = allowedDirs.some(dir => resolvedPath.startsWith(dir));
if (!isAllowed) {
throw new Error(`Access denied: ${resolvedPath}`);
}
const content = await fs.readFile(resolvedPath, 'utf-8');
const stat = await fs.stat(resolvedPath);
return { path: resolvedPath, content, size: stat.size };
});
ipcMain.handle('fs:writeFile', async (event, filePath, content) => {
await fs.writeFile(filePath, content, 'utf-8');
});
ipcMain.handle('app:getVersion', () => app.getVersion());
// on — для send (без відповіді)
ipcMain.on('window:minimize', (event) => {
BrowserWindow.fromWebContents(event.sender)?.minimize();
});
}
module.exports = { registerHandlers };
Стриминг даних через IPC
Для передачі великих обсягів:
// main/ipc-handlers.js
ipcMain.handle('fs:readLargeFile', async (event, filePath) => {
const { port1, port2 } = new MessageChannelMain();
event.sender.postMessage('port', null, [port1]);
const stream = require('fs').createReadStream(filePath);
stream.on('data', (chunk) => {
port2.postMessage({ type: 'chunk', data: chunk });
});
stream.on('end', () => {
port2.postMessage({ type: 'end' });
port2.close();
});
});
Broadcast з main у всі вікна
// main/broadcast.js
const { BrowserWindow } = require('electron');
function broadcast(channel, data) {
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send(channel, data);
}
});
}
module.exports = { broadcast };
Типові помилки: забути обробку помилок в ipcMain.handle, передавати не сериалізовані об'єкти через IPC, не валідувати шляхи в обробниках на path traversal атаки.







