Skip to content

Kapitel 15: Fortgeschrittene Techniken

In diesem Kapitel lernen Sie fortgeschrittene Techniken zur Steigerung der Entwicklungseffizienz.

15.1 Automatische Anwendungsaktualisierung

autoUpdater-Modul verwenden

Das autoUpdater-Modul ermöglicht die automatische Aktualisierung von Electron-Anwendungen.

Grundlegende Konfiguration

javascript
// main.js
const { app, autoUpdater, dialog } = require('electron');
const log = require('electron-log');

// Logging einrichten
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';

// Update-Server URL
const server = 'https://update.example.com';
const url = `${server}/update/${process.platform}/${app.getVersion()}`;
autoUpdater.setFeedURL({ url });

// Update-Ereignisse
autoUpdater.on('checking-for-update', () => {
  log.info('Suche nach Updates...');
});

autoUpdater.on('update-available', (info) => {
  log.info('Update verfügbar:', info.version);
});

autoUpdater.on('update-not-available', (info) => {
  log.info('Kein Update verfügbar:', info.version);
});

autoUpdater.on('error', (err) => {
  log.error('Fehler beim Update:', err);
});

autoUpdater.on('download-progress', (progressObj) => {
  let log_message = 'Download-Geschwindigkeit: ' + progressObj.bytesPerSecond;
  log_message = log_message + ' - Aktuell ' + progressObj.percent + '%';
  log_message = log_message + ' (' + progressObj.transferred + '/' + progressObj.total + ')';
  log.info(log_message);
});

autoUpdater.on('update-downloaded', (info) => {
  log.info('Update heruntergeladen:', info.version);
  
  // Benutzer fragen, ob neu starten
  dialog.showMessageBox({
    type: 'info',
    title: 'Update verfügbar',
    message: `Version ${info.version} wurde heruntergeladen. Möchten Sie die Anwendung jetzt neu starten, um das Update zu installieren?`,
    buttons: ['Ja', 'Später']
  }).then((returnValue) => {
    if (returnValue.response === 0) {
      autoUpdater.quitAndInstall();
    }
  });
});

// Alle 10 Minuten nach Updates suchen
setInterval(() => {
  autoUpdater.checkForUpdates();
}, 10 * 60 * 1000);

// Beim Starten nach Updates suchen
app.whenReady().then(() => {
  autoUpdater.checkForUpdates();
});

Update-Server einrichten (vereinfacht)

javascript
// update-server.js (einfacher Express-Server)
const express = require('express');
const path = require('path');

const app = express();
const PORT = 3000;

// Update-Dateien servieren
app.get('/update/:platform/:version', (req, res) => {
  const { platform, version } = req.params;
  
  let fileName;
  if (platform === 'win32') {
    fileName = 'my-app-setup.exe';
  } else if (platform === 'darwin') {
    fileName = 'my-app.dmg';
  } else {
    fileName = 'my-app.AppImage';
  }
  
  const filePath = path.join(__dirname, 'releases', fileName);
  
  res.download(filePath, (err) => {
    if (err) {
      res.status(404).json({ message: 'Kein Update verfügbar' });
    }
  });
});

app.listen(PORT, () => {
  console.log(`Update-Server läuft auf Port ${PORT}`);
});

electron-builder mit Auto-Update

json
{
  "build": {
    "publish": [
      {
        "provider": "generic",
        "url": "https://update.example.com"
      }
    ]
  }
}

15.2 Benutzerdefinierte Anwendungsicons und Startseite

Animierter Startbildschirm (Splash Screen)

javascript
// main.js
const { BrowserWindow, app } = require('electron');
const path = require('path');

function createWindow() {
  // Splash Screen erstellen
  const splash = new BrowserWindow({
    width: 400,
    height: 300,
    transparent: false,
    frame: false,
    alwaysOnTop: true,
    center: true,
    skipTaskbar: true
  });

  splash.loadFile('splash.html');

  // Hauptfenster erstellen
  const mainWin = new BrowserWindow({
    width: 1200,
    height: 800,
    show: false,  // Erst anzeigen, wenn bereit
    icon: path.join(__dirname, 'assets', 'icon.png')
  });

  // Wenn Hauptfenster bereit
  mainWin.once('ready-to-show', () => {
    setTimeout(() => {
      splash.destroy();
      mainWin.show();
    }, 2000);  // 2 Sekunden warten
  });

  mainWin.loadFile('index.html');
}

app.whenReady().then(createWindow);

splash.html

html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Lädt...</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      height: 100vh;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      font-family: Arial, sans-serif;
    }

    .logo {
      font-size: 48px;
      margin-bottom: 20px;
    }

    .progress {
      width: 80%;
      height: 4px;
      background: rgba(255, 255, 255, 0.3);
      border-radius: 2px;
      overflow: hidden;
    }

    .progress-bar {
      width: 0%;
      height: 100%;
      background: white;
      animation: progress 2s ease-in-out forwards;
    }

    @keyframes progress {
      0% { width: 0%; }
      100% { width: 100%; }
    }

    .status {
      margin-top: 20px;
      font-size: 14px;
      opacity: 0.8;
    }
  </style>
</head>
<body>
  <div class="logo">🚀</div>
  <h1>Meine App</h1>
  <div class="progress">
    <div class="progress-bar"></div>
  </div>
  <div class="status">Anwendung wird geladen...</div>
</body>
</html>

Tray-Icon mit Kontextmenü

javascript
// main.js
const { Tray, Menu, BrowserWindow } = require('electron');
const path = require('path');

let tray = null;
let mainWin;

function createTray() {
  // Tray-Icon erstellen
  tray = new Tray(path.join(__dirname, 'assets', 'tray-icon.png'));

  // Kontextmenü erstellen
  const contextMenu = Menu.buildFromTemplate([
    {
      label: 'Fenster anzeigen',
      click: () => {
        if (mainWin) {
          mainWin.show();
          mainWin.focus();
        }
      }
    },
    {
      label: 'Neue Nachricht',
      click: () => {
        // Benachrichtigung senden
        if (mainWin) {
          mainWin.webContents.send('new-message', {
            title: 'Neue Nachricht',
            body: 'Sie haben eine neue Nachricht erhalten.'
          });
        }
      }
    },
    { type: 'separator' },
    {
      label: 'Einstellungen',
      click: () => {
        if (mainWin) {
          mainWin.webContents.send('open-settings');
        }
      }
    },
    { type: 'separator' },
    {
      label: 'Beenden',
      click: () => {
        app.quit();
      }
    }
  ]);

  tray.setToolTip('Meine Electron-App');
  tray.setContextMenu(contextMenu);

  // Tray-Klick-Event
  tray.on('click', () => {
    if (mainWin.isVisible()) {
      mainWin.hide();
    } else {
      mainWin.show();
      mainWin.focus();
    }
  });
}

function createWindow() {
  mainWin = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  });

  mainWin.loadFile('index.html');
  createTray();
}

app.whenReady().then(createWindow);

15.3 Haupt- und Renderer-Prozess-Berechtigungssteuerung

Sicherheitsbeste Practices

1. Context Isolation aktivieren

javascript
// main.js - RICHTIG
const mainWin = new BrowserWindow({
  webPreferences: {
    contextIsolation: true,   // WICHTIG: Einschalten!
    nodeIntegration: false,    // WICHTIG: Ausschalten!
    preload: path.join(__dirname, 'preload.js')
  }
});

2. Preload-Skript als sichere Brücke

javascript
// preload.js
const { contextBridge, ipcRenderer } = require('electron');

// Nur spezifische APIs freigeben
contextBridge.exposeInMainWorld('api', {
  // Nur notwendige Funktionen freigeben
  saveData: (data) => ipcRenderer.invoke('save-data', data),
  loadData: () => ipcRenderer.invoke('load-data'),
  
  // Event-Listener
  onUpdateAvailable: (callback) => {
    ipcRenderer.on('update-available', (event, info) => callback(info));
  }
});

// NICHT alles freigeben!
// FALSCH:
// contextBridge.exposeInMainWorld('electron', require('electron'));

3. Content Security Policy (CSP)

html
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Security-Policy" 
        content="
          default-src 'self';
          script-src 'self';
          style-src 'self' 'unsafe-inline';
          img-src 'self' data:;
          connect-src 'self' https://api.example.com;
        ">
  <title>Meine App</title>
</head>
<body>
  <!-- Inhalt -->
</body>
</html>

4. Remote-Content isolieren

javascript
// main.js
const mainWin = new BrowserWindow({
  webPreferences: {
    // Sandbox für zusätzliche Sicherheit
    sandbox: true,
    
    // WebSecurity aktivieren
    webSecurity: true,
    
    // Keine Node.js-Integration
    nodeIntegration: false,
    nodeIntegrationInWorker: false,
    nodeIntegrationInSubFrames: false,
    
    // Context Isolation
    contextIsolation: true,
    
    // Preload-Skript
    preload: path.join(__dirname, 'preload.js')
  }
});

15.4 Verwendung von Drittanbieter-Plugins

Häufig verwendete Plugins

electron-store (Datenspeicherung)

bash
npm install electron-store
javascript
// main.js
const Store = require('electron-store');

const store = new Store();

// Daten speichern
store.set('user.name', 'Max Mustermann');
store.set('settings.theme', 'dark');

// Daten abrufen
const userName = store.get('user.name');
const theme = store.get('settings.theme', 'light');  // Standard: 'light'

// Alle Daten abrufen
const allData = store.store;

// Daten löschen
store.delete('user.name');

// Store zurücksetzen
store.clear();

electron-log (Protokollierung)

bash
npm install electron-log
javascript
// main.js
const log = require('electron-log');

// Log-Ausgabe
log.info('Anwendung gestartet');
log.warn('Warnung: ...');
log.error('Fehler: ', new Error('Ein Fehler'));

// Log-Datei finden
console.log('Log-Datei:', log.transports.file.getFile());

// Log-Level festlegen
log.transports.file.level = 'info';

// Log-Datei-Größe begrenzen
log.transports.file.maxSize = 5 * 1024 * 1024;  // 5 MB

axios (HTTP-Anfragen)

bash
npm install axios
javascript
// main.js oder preload.js
const axios = require('axios');

async function fetchData() {
  try {
    const response = await axios.get('https://api.example.com/data', {
      headers: {
        'Authorization': 'Bearer token123'
      }
    });
    
    console.log('Daten abgerufen:', response.data);
    return response.data;
  } catch (error) {
    console.error('Fehler beim Abrufen:', error.message);
    throw error;
  }
}

15.5 Electron-Versionsaktualisierung und Kompatibilität

Electron-Version aktualisieren

bash
# Electron auf neueste Version aktualisieren
npm install electron@latest --save-dev

# Auf spezifische Version aktualisieren
npm install electron@31.0.0 --save-dev

Kompatibilitätstabelle

Electron-VersionChromium-VersionNode.js-Version
31.0.012620.x
30.0.012420.x
29.0.012218.x
28.0.012018.x

Migrationsleitfaden

Von Electron 28 auf 29

javascript
// Änderungen in der neuen Version beachten
// Zum Beispiel: Veraltete APIs entfernen

// Veraltet (Electron 28):
ipcRenderer.sendSync('channel', data);

// Neu (Electron 29+):
// Verwenden Sie invoke/handle mit Promises
ipcRenderer.invoke('channel', data).then(result => {
  console.log(result);
});

Zusammenfassung

In diesem Kapitel haben Sie gelernt:

  • Automatische Anwendungsaktualisierung mit autoUpdater zu implementieren
  • Benutzerdefinierte Icons und Startseiten zu erstellen
  • Berechtigungen für Haupt- und Renderer-Prozesse sicher zu steuern
  • Drittanbieter-Plugins effektiv zu nutzen
  • Electron-Versionen zu aktualisieren und Kompatibilität zu wahren

Im nächsten Kapitel werden wir sich auf häufige Interviewfragen vorbereiten.

Frei für alle Anfänger