Eine Electron-App durchläuft eine festgelegte Sequenz von Lebens-Events: starten, Fenster zeigen, Fenster schließen, beenden. Das app-Modul liefert die Events dazu — und du musst sie richtig behandeln, sonst stirbt deine App auf macOS, wo sie nicht sterben sollte. Hier alle relevanten Events mit Reihenfolge und Plattform-Eigenheiten.
Die wichtigste Reihenfolge
| Event | Wann | Was du typisch machst |
|---|---|---|
ready | Electron initialisiert | Erstes Fenster öffnen, Menü registrieren |
activate (macOS) | Dock-Icon angeklickt, kein Fenster | Fenster öffnen, falls keines da |
window-all-closed | Alle Fenster geschlossen | macOS: nichts; Windows/Linux: app.quit() |
before-quit | Quit angefordert | Pending-Operationen abschließen |
will-quit | Kurz vor Quit | Cleanup |
quit | App beendet sich | Letzte Logs |
Standard-Skelett
import { app, BrowserWindow } from 'electron';
function createWindow() {
const win = new BrowserWindow({ width: 1200, height: 800 });
win.loadFile('index.html');
}
// App ist bereit — Fenster öffnen
app.whenReady().then(() => {
createWindow();
// macOS: wenn auf Dock-Icon geklickt und kein Fenster offen
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// Alle Fenster zu — auf macOS NICHT quitten (Apps bleiben im Hintergrund)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});Der Plattform-Check ist Pflicht — auf macOS bleiben Apps standardmäßig im Dock aktiv, auch ohne offenes Fenster. Der User reaktiviert sie über das Dock-Icon (activate-Event).
ready vs. app.whenReady()
Zwei Varianten, dasselbe zu erreichen:
// klassisch (Event-basiert)
app.on('ready', () => {
createWindow();
});
// modern (Promise-basiert) — empfohlen
app.whenReady().then(() => {
createWindow();
});
// oder mit await
await app.whenReady();
createWindow();whenReady() ist Promise-basiert — passt besser zu modernen async/await-Patterns. Wer beides registriert, doppelt sich.
before-quit vs. will-quit vs. quit
Drei sehr ähnliche Events, mit feinen Unterschieden:
| Event | Verhinderbar | Wann |
|---|---|---|
before-quit | Ja (event.preventDefault()) | Quit-Anforderung läuft, Fenster werden gleich geschlossen |
will-quit | Ja | Alle Fenster zu, App quittet gleich |
quit | Nein | App ist im Begriff zu sterben — letzte Chance für synchrone Logs |
let isQuitting = false;
app.on('before-quit', async (event) => {
if (isQuitting) return;
if (hasUnsavedChanges()) {
event.preventDefault();
const choice = await dialog.showMessageBox({
type: 'warning',
buttons: ['Speichern', 'Verwerfen', 'Abbrechen'],
message: 'Es gibt ungespeicherte Änderungen.'
});
if (choice.response === 0) {
await saveAll();
isQuitting = true;
app.quit();
} else if (choice.response === 1) {
isQuitting = true;
app.quit();
}
}
});Wichtig: das isQuitting-Flag verhindert die Endlos-Schleife (app.quit() löst wieder before-quit aus).
macOS-Spezial: window-all-closed und activate
Auf macOS bleiben Apps nach Schließen aller Fenster im Dock aktiv. User-Erwartung:
- Alle Fenster zu → App im Hintergrund, Menüleiste zeigt App-Namen, Dock-Icon bleibt
- Dock-Icon klicken → ein neues Fenster öffnen (
activate-Event) - Cmd+Q → wirklich quit
app.on('window-all-closed', () => {
// NUR auf Windows/Linux quitten
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// Beim Reaktivieren über Dock: Fenster öffnen falls keines da
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});Auf Windows und Linux ist das Verhalten anders: dort beendet sich die App, sobald das letzte Fenster zu ist — User-Erwartung ist „App = Fenster".
Single-Instance-Lock
Standardverhalten: jede gestartete Electron-App ist eine eigene Instanz. Manche Apps wollen aber nur eine Instanz erlauben — bei einem zweiten Start soll die bestehende Instanz fokussiert werden.
const gotLock = app.requestSingleInstanceLock();
if (!gotLock) {
app.quit(); // zweite Instanz: sofort beenden
} else {
app.on('second-instance', (_event, argv) => {
// Bestehende Instanz: Fenster nach vorne holen
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
// argv enthält die Command-Line der zweiten Instanz —
// praktisch für „Datei öffnen mit App"
});
app.whenReady().then(createWindow);
}Klassischer Use-Case: User klickt Datei im Finder/Explorer → falls App bereits läuft, soll sie das File im bestehenden Fenster öffnen, nicht eine zweite Instanz starten.
Spezielle App-Events
| Event | Wann |
|---|---|
open-file (macOS) | User öffnet Datei via Finder oder „Open With" |
open-url (macOS) | User klickt einen Custom-Protocol-Link |
browser-window-created | Jedes Mal wenn ein BrowserWindow erzeugt wird |
web-contents-created | Jeder neue WebContents (auch BrowserView) — Sicherheits-Hook |
gpu-process-crashed | GPU-Prozess gestorben |
child-process-gone | Renderer/utilityProcess gestorben |
web-contents-created ist der zentrale Sicherheits-Hook: hier kannst du setWindowOpenHandler, will-navigate und will-attach-webview global registrieren.
Häufige Stolperfallen
window-all-closed ohne Plattform-Check ist falsch.
Wer einfach app.quit() ruft, killt die App auf macOS bei jedem Fenster-Schließen — das fühlt sich für Mac-User falsch an. Immer mit process.platform !== 'darwin'-Check.
app.quit() in before-quit = Endlos-Schleife.
before-quit löst aus, weil quit angefordert wurde. Wenn du in dem Handler wieder app.quit() rufst, ohne Flag zu setzen: Endlos-Loop. isQuitting-Flag ist Pflicht-Pattern.
ready NICHT vor App-Quit feuern.
Wer vor whenReady() schon BrowserWindow erstellt: crasht. Der Renderer braucht voll initialisierte Electron-Runtime. Always wait for ready.
Async-Cleanup im will-quit nur bedingt möglich.
Long-running async Cleanup im will-quit blockt das Quit. Mit event.preventDefault() und app.exit() nach dem Cleanup. Für sehr langes Cleanup eher in before-quit mit User-Dialog.
Single-Instance-Lock vor jeder weiteren App-Logik.
requestSingleInstanceLock() muss VOR app.whenReady() aufgerufen werden — sonst öffnet die zweite Instanz schon ein Fenster, bevor sie merkt, dass sie sich beenden sollte.
open-file und open-url nur auf macOS.
Auf Windows kommen Datei-Argumente per Command-Line (process.argv). Auf Linux ähnlich. Cross-Platform-File-Open braucht beide Pfade: macOS-Event + argv-Parsing.
Weiterführende Ressourcen
Externe Quellen
- app — Electron API — alle Events und Methoden
- Window Management — Fenster-Verhalten je Plattform
- Process Model