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

EventWannWas du typisch machst
readyElectron initialisiertErstes Fenster öffnen, Menü registrieren
activate (macOS)Dock-Icon angeklickt, kein FensterFenster öffnen, falls keines da
window-all-closedAlle Fenster geschlossenmacOS: nichts; Windows/Linux: app.quit()
before-quitQuit angefordertPending-Operationen abschließen
will-quitKurz vor QuitCleanup
quitApp beendet sichLetzte Logs

Standard-Skelett

JavaScript main.js
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:

JavaScript
// 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:

EventVerhinderbarWann
before-quitJa (event.preventDefault())Quit-Anforderung läuft, Fenster werden gleich geschlossen
will-quitJaAlle Fenster zu, App quittet gleich
quitNeinApp ist im Begriff zu sterben — letzte Chance für synchrone Logs
JavaScript Quit verhindern bei ungespeicherten Änderungen
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
JavaScript Mac-Pattern
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.

JavaScript Single-Instance erzwingen
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

EventWann
open-file (macOS)User öffnet Datei via Finder oder „Open With"
open-url (macOS)User klickt einen Custom-Protocol-Link
browser-window-createdJedes Mal wenn ein BrowserWindow erzeugt wird
web-contents-createdJeder neue WebContents (auch BrowserView) — Sicherheits-Hook
gpu-process-crashedGPU-Prozess gestorben
child-process-goneRenderer/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

/ Weiter

Zurück zu Grundlagen

Zur Übersicht