Wenn der Main an einen Renderer pushen will, gibt es drei Patterns: an den Sender der ursprünglichen Anfrage zurück, an einen bestimmten WebContents, oder Broadcast an alle Fenster. Hier die Unterschiede mit klaren Use-Cases.
Die drei Wege
| Pattern | Wann |
|---|---|
event.sender.send(...) | Antwort an den Renderer, der die Anfrage gestellt hat |
win.webContents.send(...) | Push an einen bestimmten Renderer |
BrowserWindow.getAllWindows().forEach(win => ...) | Broadcast an alle Fenster |
event.sender.send — Antwort an den Anfrager
ipcMain.on('files:large-read-request', async (event, path) => {
const stream = fs.createReadStream(path);
stream.on('data', (chunk) => {
// Antwort GEZIELT an den anfragenden Renderer
event.sender.send('files:chunk', chunk);
});
stream.on('end', () => {
event.sender.send('files:done');
});
});event.sender ist die webContents-Instanz des Renderers, der die Nachricht geschickt hat. Bei Multi-Window-Apps sehr wichtig — sonst landet die Antwort beim falschen Fenster.
Wer das vergisst und stattdessen mainWindow.webContents.send(...) ruft, schickt die Antwort möglicherweise an ein ganz anderes Fenster als das, das die Anfrage gestartet hat.
webContents.send — gezielter Push
// Bestimmtem Fenster pushen — meist das Hauptfenster
let mainWindow;
downloadManager.on('progress', (percent) => {
mainWindow.webContents.send('download:progress', { percent });
});Der Push kommt nicht aus einem Renderer-Request — z. B. ein Background-Download, Timer, externer Event. Der Main entscheidet, welcher Renderer das hören soll.
Pattern: das Hauptfenster in einer Variable halten und gezielt ansprechen.
Broadcast an alle Fenster
// Theme-Wechsel an ALLE
nativeTheme.on('updated', () => {
const isDark = nativeTheme.shouldUseDarkColors;
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send('theme:changed', isDark);
}
});
});BrowserWindow.getAllWindows() liefert alle aktuell offenen Fenster. Der isDestroyed-Check verhindert Race-Conditions, wenn ein Fenster gerade während des Broadcasts geschlossen wird.
Pattern: Multi-Window-Sync
Klassischer Use-Case: User ändert Settings im Settings-Fenster, das Hauptfenster soll die neue Konfig sehen.
ipcMain.handle('settings:save', async (event, newSettings) => {
await store.set('settings', newSettings);
// Settings-Fenster bekommt nichts (es weiß ja schon)
// Alle ANDEREN Fenster benachrichtigen
BrowserWindow.getAllWindows().forEach(win => {
if (win.webContents.id !== event.sender.id && !win.isDestroyed()) {
win.webContents.send('settings:changed', newSettings);
}
});
return true;
});event.sender.id zeigt, von welchem WebContents die Anfrage kam — den klammern wir aus dem Broadcast aus.
webContents.fromId — gezielter Zugriff per ID
// ID merken
const importantId = mainWindow.webContents.id;
// Später gezielt darauf zugreifen
const wc = webContents.fromId(importantId);
if (wc && !wc.isDestroyed()) {
wc.send('event:ping');
}Praktisch, wenn du nicht die ganze BrowserWindow-Referenz halten willst, sondern nur eine ID. IDs sind stabil über die Lebenszeit des WebContents.
Interessantes
event.sender.send für Antworten — nicht mainWindow.webContents.send.
Klassischer Bug bei Multi-Window-Apps: man hat sich mainWindow als globale Variable gemerkt und sendet die Antwort dorthin — aber die Anfrage kam vom Settings-Fenster. Folge: das Settings-Fenster wartet ewig auf Antwort. event.sender.send ist die saubere Lösung.
isDestroyed-Check beim Broadcast.
Wenn ein Fenster gerade geschlossen wird, kann webContents.send werfen. Mit if (!win.isDestroyed()) defensiv prüfen. Bei großen Apps mit dynamischen Fenstern Pflicht.
webContents.send hat kein Return-Value.
Anders als invoke ist es Fire-and-Forget. Der Renderer hört per ipcRenderer.on. Wer Bestätigung braucht: zweiter Channel zurück, oder das ganze Pattern als invoke/handle umbauen.
WebContents-IDs sind stabil pro Instanz.
Pro BrowserWindow und WebContentsView eine eindeutige webContents.id. Bleibt während Lebenszeit konstant — gut zum Merken in Maps. Beim Schließen wird die ID frei.
Broadcast an viele Renderer = Performance-Aufmerken.
Bei Apps mit vielen Fenstern (Browser-Tab-artig) wird Broadcast langsam. Pragmatisch: nur die wirklich betroffenen Renderer benachrichtigen. Oder selber ein Pub/Sub-Modell mit topic-spezifischen Subscribers bauen.
Renderer kann nicht direkt an anderen Renderer senden.
Es gibt keine direkte webContents.sendTo(other). Wer Renderer-zu-Renderer-Kommunikation braucht: über IPC zum Main, der dann forwarded — oder MessagePort-Channel. Mehr im Artikel MessagePort.