Electron: Plattformübergreifende Desktop-Apps leicht gemacht

Veröffentlicht: 2022-03-11

Anfang dieses Jahres veröffentlichte Github Atom-Shell, den Kern seines berühmten Open-Source-Editors Atom, und benannte ihn zu diesem besonderen Anlass in Electron um.

Electron bringt im Gegensatz zu anderen Wettbewerbern in der Kategorie der Node.js-basierten Desktop-Anwendungen seine eigene Wendung in diesen bereits gut etablierten Markt, indem es die Leistungsfähigkeit von Node.js (io.js bis zu den letzten Releases) mit der Chromium Engine kombiniert uns das Beste aus server- und clientseitigem JavaScript.

Stellen Sie sich eine Welt vor, in der wir leistungsstarke, datengesteuerte, plattformübergreifende Desktop-Anwendungen erstellen könnten, die nicht nur auf dem ständig wachsenden Repository von NPM-Modulen, sondern auch auf der gesamten Bower-Registrierung basieren, um alle unsere clientseitigen Anforderungen zu erfüllen.

Geben Sie Elektron ein.

Erstellen von plattformübergreifenden Desktop-Apps mit Electron

Erstellen von plattformübergreifenden Desktop-Apps mit Electron
Twittern

In diesem Tutorial erstellen wir eine einfache Kennwort-Schlüsselbundanwendung mit Electron, Angular.js und Loki.js, einer leichten und speicherinternen Datenbank mit einer vertrauten Syntax für MongoDB-Entwickler.

Der vollständige Quellcode für diese Anwendung ist hier verfügbar.

Dieses Tutorial geht davon aus, dass:

  • Der Leser hat Node.js und Bower auf seinem Computer installiert.
  • Sie sind mit Node.js, Angular.js und MongoDB-ähnlicher Abfragesyntax vertraut.

Waren bekommen

Das Wichtigste zuerst: Wir müssen die Electron-Binärdateien abrufen, um unsere App lokal zu testen. Wir können es global installieren und als CLI verwenden oder lokal im Pfad unserer Anwendung installieren. Ich empfehle, es global zu installieren, damit wir es nicht für jede App, die wir entwickeln, immer wieder tun müssen.

Wir werden später lernen, wie wir unsere Anwendung für die Verteilung mit Gulp packen. Dieser Prozess beinhaltet das Kopieren der Electron-Binärdateien, und daher macht es wenig bis gar keinen Sinn, sie manuell im Pfad unserer Anwendung zu installieren.

Um die Electron CLI zu installieren, können wir den folgenden Befehl in unser Terminal eingeben:

 $ npm install -g electron-prebuilt

Um die Installation zu testen, geben Sie electron -h ein und es sollte die Version der Electron CLI anzeigen.

Zum Zeitpunkt der Erstellung dieses Artikels war die Version von Electron 0.31.2 .

Einrichten des Projekts

Gehen wir von folgender grundlegender Ordnerstruktur aus:

 my-app |- cache/ |- dist/ |- src/ |-- app.js | gulpfile.js

… wobei: - cache/ verwendet wird, um die Electron-Binärdateien beim Erstellen der App herunterzuladen. - dist/ enthält die generierten Distributionsdateien. - src/ enthält unseren Quellcode. - src/app.js wird der Einstiegspunkt unserer Anwendung sein.

Als nächstes navigieren wir zum Ordner src/ in unserem Terminal und erstellen die Dateien package.json und bower.json für unsere App:

 $ npm init $ bower init

Wir werden die erforderlichen Pakete später in diesem Tutorial installieren.

Elektronenprozesse verstehen

Electron unterscheidet zwei Arten von Prozessen:

  • Der Hauptprozess : Der Einstiegspunkt unserer Anwendung, die Datei, die ausgeführt wird, wenn wir die App ausführen. In der Regel deklariert diese Datei die verschiedenen Fenster der App und kann optional verwendet werden, um globale Ereignis-Listener mithilfe des IPC-Moduls von Electron zu definieren.
  • Der Renderer-Prozess : Der Controller für ein bestimmtes Fenster in unserer Anwendung. Jedes Fenster erstellt seinen eigenen Renderer-Prozess.

Aus Gründen der Codeklarheit sollte für jeden Renderer-Prozess eine separate Datei verwendet werden. Um den Hauptprozess für unsere App zu definieren, öffnen wir src/app.js und fügen das app -Modul zum Starten der App und das browser-window zum Erstellen der verschiedenen Fenster unserer App ein (beide Teil des Electron-Kerns). so wie:

 var app = require('app'), BrowserWindow = require('browser-window');

Wenn die App tatsächlich gestartet wird, löst sie ein ready Ereignis aus, an das wir binden können. An dieser Stelle können wir das Hauptfenster unserer App instanziieren:

 var mainWindow = null; app.on('ready', function() { mainWindow = new BrowserWindow({ width: 1024, height: 768 }); mainWindow.loadUrl('file://' + __dirname + '/windows/main/main.html'); mainWindow.openDevTools(); });

Kernpunkte:

  • Wir erstellen ein neues Fenster, indem wir eine neue Instanz des BrowserWindow Objekts erstellen.
  • Es nimmt ein Objekt als ein einziges Argument, was es uns ermöglicht, verschiedene Einstellungen zu definieren, darunter die Standardbreite und -höhe des Fensters .
  • Die Fensterinstanz hat eine loadUrl() -Methode, die es uns ermöglicht, den Inhalt einer tatsächlichen HTML-Datei in das aktuelle Fenster zu laden. Die HTML-Datei kann entweder lokal oder remote sein.
  • Die Fensterinstanz verfügt über eine optionale openDevTools() -Methode, die es uns ermöglicht, eine Instanz der Chrome-Entwicklungstools im aktuellen Fenster zu Debugging-Zwecken zu öffnen.

Als nächstes sollten wir unseren Code ein wenig organisieren. Ich empfehle, einen windows/ -Ordner in unserem src/ -Ordner zu erstellen, in dem wir für jedes Fenster einen Unterordner erstellen können:

 my-app |- src/ |-- windows/ |--- main/ |---- main.controller.js |---- main.html |---- main.view.js

… wobei main.controller.js die „serverseitige“ Logik unserer Anwendung und main.view.js die „clientseitige“ Logik unserer Anwendung enthalten wird.

Die Datei main.html ist einfach eine HTML5-Webseite, also können wir sie einfach so starten:

 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Password Keychain</title> </head> <body> <h1>Password Keychain</h1> </body> </html>

An diesem Punkt sollte unsere App betriebsbereit sein. Um es zu testen, können wir einfach Folgendes in unser Terminal im Stammverzeichnis des src -Ordners eingeben:

 $ electron .

Wir können diesen Prozess automatisieren, indem wir das start der Datei package.son definieren.

Erstellen einer Desktop-App für den Kennwortschlüsselbund

Um eine Passwortschlüsselbund-Anwendung zu erstellen, brauchen wir: - Eine Möglichkeit, Passwörter hinzuzufügen, zu generieren und zu speichern. - Ein bequemer Weg, Passwörter zu kopieren und zu entfernen.

Passwörter generieren und speichern

Ein einfaches Formular genügt, um neue Passwörter einzugeben. Um die Kommunikation zwischen mehreren Fenstern in Electron zu demonstrieren, fügen Sie zunächst ein zweites Fenster in unserer Anwendung hinzu, das das Formular „Einfügen“ anzeigt. Da wir dieses Fenster mehrmals öffnen und schließen, sollten wir die Logik in einer Methode verpacken, damit wir sie bei Bedarf einfach aufrufen können:

 function createInsertWindow() { insertWindow = new BrowserWindow({ width: 640, height: 480, show: false }); insertWindow.loadUrl('file://' + __dirname + '/windows/insert/insert.html'); insertWindow.on('closed',function() { insertWindow = null; }); }

Kernpunkte:

  • Wir müssen die show- Eigenschaft im options-Objekt des BrowserWindow-Konstruktors auf false setzen, um zu verhindern, dass das Fenster standardmäßig geöffnet ist, wenn die Anwendungen gestartet werden.
  • Wir müssen die BrowserWindow-Instanz immer dann zerstören, wenn das Fenster ein geschlossenes Ereignis auslöst.

Öffnen und Schließen des „Einfügen“-Fensters

Die Idee ist, das „Einfügen“-Fenster auslösen zu können, wenn der Endbenutzer auf eine Schaltfläche im „Haupt“-Fenster klickt. Dazu müssen wir eine Nachricht vom Hauptfenster an den Hauptprozess senden, um ihn anzuweisen, das Einfügefenster zu öffnen. Wir können dies mit dem IPC-Modul von Electron erreichen. Es gibt eigentlich zwei Varianten des IPC-Moduls:

  • Einer für den Hauptprozess, der es der App ermöglicht, von Windows gesendete Nachrichten zu abonnieren.
  • Einer für den Renderer-Prozess, der es der App ermöglicht, Nachrichten an den Hauptprozess zu senden.

Obwohl der Kommunikationskanal von Electron größtenteils unidirektional ist, ist es möglich, auf das IPC-Modul des Hauptprozesses in einem Renderer-Prozess zuzugreifen, indem das Remote-Modul verwendet wird. Außerdem kann der Hauptprozess mithilfe der Methode Event.sender.send() eine Nachricht an den Renderer-Prozess zurücksenden, von dem das Ereignis stammt.

Um das IPC-Modul zu verwenden, benötigen wir es wie jedes andere NPM-Modul in unserem Main Process-Skript:

 var ipc = require('ipc');

… und dann mit der Methode on() an Events binden:

 ipc.on('toggle-insert-view', function() { if(!insertWindow) { createInsertWindow(); } return (!insertWindow.isClosed() && insertWindow.isVisible()) ? insertWindow.hide() : insertWindow.show(); });

Wichtige Punkte:

  • Wir können das Ereignis beliebig benennen, das Beispiel ist nur willkürlich.
  • Vergessen Sie nicht zu überprüfen, ob die BrowserWindow-Instanz bereits erstellt wurde, falls nicht, instanziieren Sie sie.
  • Die BrowserWindow-Instanz hat einige nützliche Methoden:
    • isClosed() gibt einen booleschen Wert zurück, unabhängig davon, ob sich das Fenster derzeit in einem closed Zustand befindet oder nicht.
    • isVisible() : gibt einen booleschen Wert zurück, ob das Fenster derzeit sichtbar ist oder nicht.
    • show() / hide() : bequeme Methoden zum Ein- und Ausblenden des Fensters.

Jetzt müssen wir dieses Ereignis tatsächlich aus dem Renderer-Prozess auslösen. Wir erstellen eine neue Skriptdatei namens main.view.js und fügen sie unserer HTML-Seite hinzu, wie wir es mit jedem normalen Skript tun würden:

 <script src="./main.view.js"></script>

Das Laden der Skriptdatei über das HTML- script -Tag lädt diese Datei in einem clientseitigen Kontext. Das bedeutet, dass beispielsweise globale Variablen über window.<varname> verfügbar sind. Um ein Skript in einem serverseitigen Kontext zu laden, können wir die require() Methode direkt in unserer HTML-Seite verwenden: require('./main.controller.js'); .

Auch wenn das Skript im clientseitigen Kontext geladen wird, können wir dennoch auf das IPC-Modul für den Renderer-Prozess auf die gleiche Weise wie für den Hauptprozess zugreifen und dann unser Ereignis als solches senden:

 var ipc = require('ipc'); angular .module('Utils', []) .directive('toggleInsertView', function() { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); ipc.send('toggle-insert-view'); }); }; });

Es ist auch eine sendSync()-Methode verfügbar, falls wir unsere Ereignisse synchron senden müssen.

Jetzt müssen wir zum Öffnen des „Einfügen“-Fensters nur noch eine HTML-Schaltfläche mit der passenden Angular-Direktive erstellen:

 <div ng-controller="MainCtrl as vm"> <button toggle-insert-view class="mdl-button"> <i class="material-icons">add</i> </button> </div>

Und fügen Sie diese Direktive als Abhängigkeit des Angular-Controllers des Hauptfensters hinzu:

 angular .module('MainWindow', ['Utils']) .controller('MainCtrl', function() { var vm = this; }); 

Passwörter generieren

Um die Dinge einfach zu halten, können wir einfach das NPM- uuid -Modul verwenden, um eindeutige IDs zu generieren, die für den Zweck dieses Tutorials als Passwörter dienen. Wir können es wie jedes andere NPM-Modul installieren, es in unserem 'Utils'-Skript anfordern und dann eine einfache Factory erstellen, die eine eindeutige ID zurückgibt:

 var uuid = require('uuid'); angular .module('Utils', []) ... .factory('Generator', function() { return { create: function() { return uuid.v4(); } }; })

Jetzt müssen wir nur noch eine Schaltfläche in der Einfügeansicht erstellen und eine Direktive daran anhängen, die auf Klickereignisse auf der Schaltfläche lauscht und die Methode create() aufruft:

 <!-- in insert.html --> <button generate-password class="mdl-button">generate</button>
 // in Utils.js angular .module('Utils', []) ... .directive('generatePassword', ['Generator', function(Generator) { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); if(!scope.vm.formData) scope.vm.formData = {}; scope.vm.formData.password = Generator.create(); scope.$apply(); }); }; }])

Passwörter speichern

An dieser Stelle möchten wir unsere Passwörter speichern. Die Datenstruktur für unsere Passworteinträge ist ziemlich einfach:

 { "id": String "description": String, "username": String, "password": String }

Alles, was wir wirklich brauchen, ist eine Art In-Memory-Datenbank, die optional zur Sicherung mit einer Datei synchronisiert werden kann. Für diesen Zweck scheint Loki.js der ideale Kandidat zu sein. Es tut genau das, was wir für den Zweck dieser Anwendung benötigen, und bietet darüber hinaus die Funktion „ Dynamische Ansichten “, mit der wir Dinge tun können, die dem Aggregationsmodul von MongoDB ähneln.

Dynamische Ansichten bieten nicht alle Funktionen, die das Aggregationsmodul von MongodDB bietet. Weitere Informationen finden Sie in der Dokumentation.

Beginnen wir mit der Erstellung eines einfachen HTML-Formulars:

 <div class="insert" ng-controller="InsertCtrl as vm"> <form name="insertForm" no-validate> <fieldset ng-disabled="!vm.loaded"> <div class="mdl-textfield"> <input class="mdl-textfield__input" type="text" ng-model="vm.formData.description" required /> <label class="mdl-textfield__label" for="description">Description...</label> </div> <div class="mdl-textfield"> <input class="mdl-textfield__input" type="text" ng-model="vm.formData.username" /> <label class="mdl-textfield__label" for="username">Username...</label> </div> <div class="mdl-textfield"> <input class="mdl-textfield__input" type="password" ng-model="vm.formData.password" required /> <label class="mdl-textfield__label" for="password">Password...</label> </div> <div class=""> <button generate-password class="mdl-button">generate</button> <button toggle-insert-view class="mdl-button">cancel</button> <button save-password class="mdl-button" ng-disabled="insertForm.$invalid">save</button> </div> </fieldset> </form> </div>

Und jetzt fügen wir die JavaScript-Logik hinzu, um das Posten und Speichern des Formularinhalts zu handhaben:

 var loki = require('lokijs'), path = require('path'); angular .module('Utils', []) ... .service('Storage', ['$q', function($q) { this.db = new loki(path.resolve(__dirname, '../..', 'app.db')); this.collection = null; this.loaded = false; this.init = function() { var d = $q.defer(); this.reload() .then(function() { this.collection = this.db.getCollection('keychain'); d.resolve(this); }.bind(this)) .catch(function(e) { // create collection this.db.addCollection('keychain'); // save and create file this.db.saveDatabase(); this.collection = this.db.getCollection('keychain'); d.resolve(this); }.bind(this)); return d.promise; }; this.addDoc = function(data) { var d = $q.defer(); if(this.isLoaded() && this.getCollection()) { this.getCollection().insert(data); this.db.saveDatabase(); d.resolve(this.getCollection()); } else { d.reject(new Error('DB NOT READY')); } return d.promise; }; }) .directive('savePassword', ['Storage', function(Storage) { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); if(scope.vm.formData) { Storage .addDoc(scope.vm.formData) .then(function() { // reset form & close insert window scope.vm.formData = {}; ipc.send('toggle-insert-view'); }); } }); }; }])

Wichtige Punkte:

  • Zuerst müssen wir die Datenbank initialisieren. Dieser Prozess umfasst das Erstellen einer neuen Instanz des Loki-Objekts, das Bereitstellen des Pfads zur Datenbankdatei als Argument, das Nachschlagen, ob diese Sicherungsdatei existiert, das Erstellen bei Bedarf (einschließlich der Sammlung „Schlüsselbund“) und das Laden des Inhalts von diese Datei im Speicher.
  • Wir können eine bestimmte Sammlung in der Datenbank mit der Methode getCollection() .
  • Ein Sammlungsobjekt stellt mehrere Methoden zur Verfügung, darunter eine insert() -Methode, mit der wir der Sammlung ein neues Dokument hinzufügen können.
  • Um den Datenbankinhalt in einer Datei zu speichern, macht das Loki-Objekt eine saveDatabase() Methode verfügbar.
  • Wir müssen die Daten des Formulars zurücksetzen und ein IPC-Ereignis senden, um den Hauptprozess anzuweisen, das Fenster zu schließen, sobald das Dokument gespeichert ist.

Wir haben jetzt ein einfaches Formular, mit dem wir neue Passwörter generieren und speichern können. Kehren wir zur Hauptansicht zurück, um diese Einträge aufzulisten.

Passwörter auflisten

Hier müssen einige Dinge passieren:

  • Wir müssen in der Lage sein, alle Dokumente in unserer Sammlung zu bekommen.
  • Wir müssen die Hauptansicht jedes Mal informieren, wenn ein neues Passwort gespeichert wird, damit sie die Ansicht aktualisieren kann.

Wir können die Liste der Dokumente abrufen, indem wir die Methode getCollection() für das Loki-Objekt aufrufen. Diese Methode gibt ein Objekt mit einer Eigenschaft namens data zurück, die einfach ein Array aller Dokumente in dieser Sammlung ist:

 this.getCollection = function() { this.collection = this.db.getCollection('keychain'); return this.collection; }; this.getDocs = function() { return (this.getCollection()) ? this.getCollection().data : null; };

Wir können dann getDocs() in unserem Angular-Controller aufrufen und alle in der Datenbank gespeicherten Passwörter abrufen, nachdem wir sie initialisiert haben:

 angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); }); });

Ein bisschen Angular-Template, und wir haben eine Passwortliste:

 <tr ng-repeat="item in vm.keychain track by $index" class="item--{{$index}}"> <td class="mdl-data-table__cell--non-numeric">{{item.description}}</td> <td>{{item.username || 'n/a'}}</td> <td> <span ng-repeat="n in [1,2,3,4,5,6]">&bull;</span> </td> <td> <a href="#" copy-password="{{$index}}">copy</a> <a href="#" remove-password="{{item}}">remove</a> </td> </tr> 

Eine nette zusätzliche Funktion wäre, die Liste der Passwörter nach dem Einfügen eines neuen zu aktualisieren. Dafür können wir das IPC-Modul von Electron verwenden. Wie bereits erwähnt, kann das IPC-Modul des Hauptprozesses in einem Renderer-Prozess aufgerufen werden, um ihn mithilfe des Remote-Moduls in einen Listener-Prozess umzuwandeln. Hier ist ein Beispiel zur Implementierung in main.view.js :

 var remote = require('remote'), remoteIpc = remote.require('ipc'); angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); remoteIpc.on('update-main-view', function() { Storage .reload() .then(function() { vm.keychain = db.getDocs(); }); }); }); }]);

Wichtige Punkte:

  • Wir müssen das Remote-Modul über seine eigene require() Methode verwenden, um das Remote-IPC-Modul vom Hauptprozess anzufordern.
  • Wir können dann unseren Renderer-Prozess über die Methode on() als Ereignis-Listener einrichten und Callback-Funktionen an diese Ereignisse binden.

Die Einfügeansicht ist dann dafür verantwortlich, dieses Ereignis auszulösen, wenn ein neues Dokument gespeichert wird:

 Storage .addDoc(scope.vm.formData) .then(function() { // refresh list in main view ipc.send('update-main-view'); // reset form & close insert window scope.vm.formData = {}; ipc.send('toggle-insert-view'); });

Passwörter kopieren

Es ist in der Regel nicht sinnvoll, Kennwörter im Klartext anzuzeigen. Stattdessen werden wir uns verstecken und eine praktische Schaltfläche bereitstellen, die es dem Endbenutzer ermöglicht, das Passwort direkt für einen bestimmten Eintrag zu kopieren.

Auch hier kommt Electron zu unserer Rettung, indem es uns ein Zwischenablagemodul mit einfachen Methoden zum Kopieren und Einfügen nicht nur von Textinhalten, sondern auch von Bildern und HTML-Code zur Verfügung stellt:

 var clipboard = require('clipboard'); angular .module('Utils', []) ... .directive('copyPassword', [function() { return function(scope, el, attrs) { el.bind('click', function(e) { e.preventDefault(); var text = (scope.vm.keychain[attrs.copyPassword]) ? scope.vm.keychain[attrs.copyPassword].password : ''; // atom's clipboard module clipboard.clear(); clipboard.writeText(text); }); }; }]);

Da das generierte Passwort eine einfache Zeichenfolge ist, können wir die Methode writeText() verwenden, um das Passwort in die Zwischenablage des Systems zu kopieren. Wir können dann den HTML-Code unserer Hauptansicht aktualisieren und die Copy-Schaltfläche mit der copy-password Direktive hinzufügen, die den Index des Arrays von Passwörtern bereitstellt:

 <a href="#" copy-password="{{$index}}">copy</a>

Passwörter entfernen

Unsere Endbenutzer möchten möglicherweise auch Passwörter löschen können, falls sie veraltet sind. Dazu müssen wir lediglich die Methode remove() für die Schlüsselbundsammlung aufrufen. Wir müssen das gesamte Dokument für die Methode „remove()“ bereitstellen:

 this.removeDoc = function(doc) { return function() { var d = $q.defer(); if(this.isLoaded() && this.getCollection()) { // remove the doc from the collection & persist changes this.getCollection().remove(doc); this.db.saveDatabase(); // inform the insert view that the db content has changed ipc.send('reload-insert-view'); d.resolve(true); } else { d.reject(new Error('DB NOT READY')); } return d.promise; }.bind(this); };

Die Loki.js-Dokumentation besagt, dass wir ein Dokument auch anhand seiner ID entfernen können, aber es scheint nicht wie erwartet zu funktionieren.

Erstellen eines Desktop-Menüs

Electron lässt sich nahtlos in unsere OS-Desktop-Umgebung integrieren, um unseren Apps ein „natives“ Erscheinungsbild für die Benutzererfahrung zu bieten. Daher wird Electron mit einem Menümodul geliefert, das der Erstellung komplexer Desktop-Menüstrukturen für unsere App gewidmet ist.

Das Menümodul ist ein umfangreiches Thema und verdient fast ein eigenes Tutorial. Ich empfehle Ihnen dringend, Electrons Desktop Environment Integration-Lernprogramm durchzulesen, um alle Funktionen dieses Moduls zu entdecken.

Für den Umfang dieses aktuellen Tutorials werden wir sehen, wie man ein benutzerdefiniertes Menü erstellt, ihm einen benutzerdefinierten Befehl hinzufügt und den Standardbefehl zum Beenden implementiert.

Erstellen und Zuweisen eines benutzerdefinierten Menüs zu unserer App

Normalerweise gehört die JavaScript-Logik für ein Electron-Menü in die Hauptskriptdatei unserer App, in der unser Hauptprozess definiert ist. Wir können es jedoch in eine separate Datei abstrahieren und über das Remote-Modul auf das Menümodul zugreifen:

 var remote = require('remote'), Menu = remote.require('menu');

Um ein einfaches Menü zu definieren, müssen wir die Methode buildFromTemplate() verwenden:

 var appMenu = Menu.buildFromTemplate([ { label: 'Electron', submenu: [{ label: 'Credits', click: function() { alert('Built with Electron & Loki.js.'); } }] } ]);

Der erste Eintrag im Array wird immer als „Standard“-Menüeintrag verwendet.

Der Wert der label Eigenschaft spielt für das Standardmenüelement keine große Rolle. Im Entwicklermodus wird immer Electron angezeigt. Wir werden später sehen, wie man dem Standardmenüpunkt während der Erstellungsphase einen benutzerdefinierten Namen zuweist.

Schließlich müssen wir dieses benutzerdefinierte Menü mit der Methode setApplicationMenu() als Standardmenü für unsere App zuweisen:

 Menu.setApplicationMenu(appMenu);

Tastenkombinationen zuordnen

Electron bietet „Beschleuniger“, eine Reihe vordefinierter Zeichenfolgen, die tatsächlichen Tastaturkombinationen zugeordnet sind, z. B.: Command+A oder Ctrl+Shift+Z .

Der Command funktioniert nicht unter Windows oder Linux. Für unsere Passwort-Schlüsselbund-Anwendung sollten wir einen File -Menüpunkt hinzufügen, der zwei Befehle anbietet:

  • Passwort erstellen : Öffnen Sie die Einfügeansicht mit Cmd (oder Strg) + N
  • Beenden : Beenden Sie die App vollständig mit Cmd (oder Strg) + Q
 ... { label: 'File', submenu: [ { label: 'Create Password', accelerator: 'CmdOrCtrl+N', click: function() { ipc.send('toggle-insert-view'); } }, { type: 'separator' // to create a visual separator }, { label: 'Quit', accelerator: 'CmdOrCtrl+Q', selector: 'terminate:' // OS X only!!! } ] } ...

Wichtige Punkte:

  • Wir können ein visuelles Trennzeichen hinzufügen, indem wir dem Array ein Element hinzufügen, dessen Eigenschaft type auf separator gesetzt ist.
  • Der CmdOrCtrl Beschleuniger ist sowohl mit Mac- als auch mit PC-Tastaturen kompatibel
  • Die selector -Eigenschaft ist nur OSX-kompatibel!

Styling unserer App

Sie haben wahrscheinlich in den verschiedenen Codebeispielen Verweise auf Klassennamen bemerkt, die mit mdl- beginnen. Für die Zwecke dieses Tutorials habe ich mich für die Verwendung des UI-Frameworks von Material Design Lite entschieden, Sie können jedoch auch ein beliebiges UI-Framework Ihrer Wahl verwenden.

Alles, was wir mit HTML5 machen können, kann in Electron gemacht werden; Denken Sie nur an die wachsende Größe der Binärdateien der App und die daraus resultierenden Leistungsprobleme, die auftreten können, wenn Sie zu viele Bibliotheken von Drittanbietern verwenden.

Verpacken von Electron-Apps für den Vertrieb

Sie haben eine Electron-App erstellt, sie sieht großartig aus, Sie haben Ihre e2e-Tests mit Selenium und WebDriver geschrieben, und Sie sind bereit, sie an die Welt zu verteilen!

Aber Sie möchten es trotzdem personalisieren, ihm einen anderen Namen als den Standardnamen „Elektron“ geben und vielleicht auch benutzerdefinierte Anwendungssymbole für Mac- und PC-Plattformen bereitstellen.

Bauen mit Gulp

Heutzutage gibt es ein Gulp-Plugin für alles, was uns einfällt. Alles, was ich tun musste, war, gulp electron in Google einzugeben, und tatsächlich gibt es ein Gulp-Electron-Plugin!

Dieses Plugin ist ziemlich einfach zu verwenden, solange die zu Beginn dieses Tutorials beschriebene Ordnerstruktur beibehalten wurde. Wenn nicht, müssen Sie die Dinge möglicherweise ein wenig verschieben.

Dieses Plugin kann wie jedes andere Gulp-Plugin installiert werden:

 $ npm install gulp-electron --save-dev

Und dann können wir unsere Gulp-Aufgabe wie folgt definieren:

 var gulp = require('gulp'), electron = require('gulp-electron'), info = require('./src/package.json'); gulp.task('electron', function() { gulp.src("") .pipe(electron({ src: './src', packageJson: info, release: './dist', cache: './cache', version: 'v0.31.2', packaging: true, platforms: ['win32-ia32', 'darwin-x64'], platformResources: { darwin: { CFBundleDisplayName: info.name, CFBundleIdentifier: info.bundle, CFBundleName: info.name, CFBundleVersion: info.version }, win: { "version-string": info.version, "file-version": info.version, "product-version": info.version } } })) .pipe(gulp.dest("")); });

Wichtige Punkte:

  • Der Ordner src/ darf nicht derselbe sein wie der Ordner, in dem sich Gulpfile.js befindet, und auch nicht derselbe Ordner wie der Distributionsordner.
  • Wir können die Plattformen, auf die wir exportieren möchten, über das platforms -Array definieren.
  • Wir sollten einen cache -Ordner definieren, in den die Electron-Binärdateien heruntergeladen werden, damit sie mit unserer App gepackt werden können.
  • Der Inhalt der Datei „package.json“ der App muss über die Eigenschaft „ packageJson “ an die gulp-Aufgabe übergeben werden.
  • Es gibt eine optionale packaging , die es uns ermöglicht, auch Zip-Archive der generierten Apps zu erstellen.
  • Für jede Plattform gibt es einen anderen Satz von „Plattformressourcen“, die definiert werden können.

Hinzufügen von App-Symbolen

Eine der Eigenschaften von platformResources ist die Eigenschaft icon , mit der wir ein benutzerdefiniertes Symbol für unsere App definieren können:

 "icon": "keychain.ico"

OS X erfordert Symbole mit der .icns . Es gibt mehrere Online-Tools, mit denen wir .png -Dateien kostenlos in .ico und .icns konvertieren können.

Fazit

In diesem Artikel haben wir nur an der Oberfläche dessen gekratzt, was Electron tatsächlich kann. Denken Sie an großartige Apps wie Atom oder Slack als Inspirationsquelle, wo Sie mit diesem Tool hingehen können.

Ich hoffe, Sie fanden dieses Tutorial nützlich. Bitte hinterlassen Sie Ihre Kommentare und teilen Sie Ihre Erfahrungen mit Electron!