Electron: łatwe międzyplatformowe aplikacje na komputery stacjonarne
Opublikowany: 2022-03-11Na początku tego roku Github wypuścił Atom-Shell, rdzeń słynnego edytora Atom i przemianował go na Electron na specjalną okazję.
Electron, w przeciwieństwie do innych konkurentów w kategorii aplikacji komputerowych opartych na Node.js, wnosi swój własny zwrot na ten już ugruntowany rynek, łącząc moc Node.js (do niedawnych wersji io.js) z silnikiem Chromium, aby wprowadzić nam to, co najlepsze w JavaScript zarówno po stronie serwera, jak i klienta.
Wyobraź sobie świat, w którym moglibyśmy budować wydajne, oparte na danych, wieloplatformowe aplikacje desktopowe, zasilane nie tylko stale rosnącym repozytorium modułów NPM, ale także całym rejestrem Bower, aby spełnić wszystkie nasze potrzeby po stronie klienta.
Wprowadź elektron.
W tym samouczku zbudujemy prostą aplikację pęku kluczy haseł przy użyciu Electron, Angular.js i Loki.js, lekkiej bazy danych w pamięci ze znaną składnią dla programistów MongoDB.
Pełny kod źródłowy tej aplikacji jest dostępny tutaj.
Ten samouczek zakłada, że:
- Czytnik ma zainstalowane Node.js i Bower na swoim komputerze.
- Znają składnię zapytań podobną do Node.js, Angular.js i MongoDB.
Zdobycie towarów
Po pierwsze, będziemy musieli pobrać pliki binarne Electron, aby przetestować naszą aplikację lokalnie. Możemy zainstalować go globalnie i używać go jako CLI lub zainstalować lokalnie w ścieżce naszej aplikacji. Polecam zainstalować go globalnie, dzięki czemu nie musimy tego robić w kółko dla każdej tworzonej przez nas aplikacji.
Dowiemy się później, jak spakować naszą aplikację do dystrybucji za pomocą Gulpa. Ten proces obejmuje kopiowanie plików binarnych Electron, dlatego nie ma sensu ręczne instalowanie go w ścieżce naszej aplikacji.
Aby zainstalować Electron CLI, możemy w naszym terminalu wpisać następujące polecenie:
$ npm install -g electron-prebuilt
Aby przetestować instalację, wpisz electron -h
i powinna wyświetlić wersję Electron CLI.
W momencie pisania tego artykułu wersja Electron była 0.31.2
.
Konfiguracja projektu
Przyjmijmy następującą podstawową strukturę folderów:
my-app |- cache/ |- dist/ |- src/ |-- app.js | gulpfile.js
… gdzie: - cache/ będzie używany do pobierania plików binarnych Electron podczas budowania aplikacji. - dist/ będzie zawierał wygenerowane pliki dystrybucyjne. - src/ będzie zawierał nasz kod źródłowy. - src/app.js będzie punktem wejścia do naszej aplikacji.
Następnie przejdziemy do folderu src/
w naszym terminalu i utworzymy pliki package.json
i bower.json
dla naszej aplikacji:
$ npm init $ bower init
Niezbędne pakiety zainstalujemy w dalszej części tego samouczka.
Zrozumienie procesów elektronowych
Electron rozróżnia dwa rodzaje procesów:
- Główny proces : punkt wejścia naszej aplikacji, plik, który będzie wykonywany przy każdym uruchomieniu aplikacji. Zazwyczaj ten plik deklaruje różne okna aplikacji i może opcjonalnie służyć do definiowania globalnych detektorów zdarzeń przy użyciu modułu IPC firmy Electron.
- Proces renderowania : Kontroler dla danego okna w naszej aplikacji. Każde okno tworzy własny proces renderowania.
Aby zapewnić przejrzystość kodu, dla każdego procesu renderowania należy użyć oddzielnego pliku. Aby zdefiniować proces główny dla naszej aplikacji, otworzymy
src/app.js
i dodamy modułapp
do uruchamiania aplikacji oraz modułbrowser-window
do tworzenia różnych okien naszej aplikacji (oba części rdzenia Electron), takie jak:
var app = require('app'), BrowserWindow = require('browser-window');
Kiedy aplikacja jest faktycznie uruchomiona, uruchamia ready
zdarzenie, z którym możemy się powiązać. W tym momencie możemy utworzyć instancję głównego okna naszej aplikacji:
var mainWindow = null; app.on('ready', function() { mainWindow = new BrowserWindow({ width: 1024, height: 768 }); mainWindow.loadUrl('file://' + __dirname + '/windows/main/main.html'); mainWindow.openDevTools(); });
Kluczowe punkty:
- Nowe okno tworzymy tworząc nową instancję obiektu
BrowserWindow
. - Przyjmuje obiekt jako pojedynczy argument, co pozwala nam zdefiniować różne ustawienia, między innymi domyślną szerokość i wysokość okna.
- Instancja okna posiada
loadUrl()
, która pozwala nam załadować zawartość aktualnego pliku HTML do bieżącego okna. Plik HTML może być lokalny lub zdalny . - Instancja okna ma opcjonalną
openDevTools()
, która pozwala nam otworzyć instancję Chrome Dev Tools w bieżącym oknie w celu debugowania.
Następnie powinniśmy trochę uporządkować nasz kod. Polecam utworzenie folderu windows/
w naszym folderze src/
, gdzie możemy utworzyć podfolder dla każdego okna, jako taki:
my-app |- src/ |-- windows/ |--- main/ |---- main.controller.js |---- main.html |---- main.view.js
… gdzie main.controller.js
będzie zawierał logikę „po stronie serwera” naszej aplikacji, a main.view.js
będzie zawierał logikę „po stronie klienta” naszej aplikacji.
Plik main.html
to po prostu strona HTML5, więc możemy po prostu uruchomić go w ten sposób:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Password Keychain</title> </head> <body> <h1>Password Keychain</h1> </body> </html>
W tym momencie nasza aplikacja powinna być gotowa do uruchomienia. Aby to przetestować, możemy po prostu wpisać w naszym terminalu, w katalogu głównym folderu src
:
$ electron .
Możemy zautomatyzować ten proces, definiując skrypt
start
pliku package.son.
Tworzenie aplikacji na komputer z pękiem kluczy z hasłem
Aby zbudować aplikację pęku kluczy haseł, potrzebujemy: - Sposób na dodawanie, generowanie i zapisywanie haseł. - Wygodny sposób kopiowania i usuwania haseł.
Generowanie i zapisywanie haseł
Do wprowadzenia nowych haseł wystarczy prosty formularz. W celu zademonstrowania komunikacji między wieloma oknami w Electronie zacznij od dodania drugiego okna w naszej aplikacji, które wyświetli formularz „wstaw”. Ponieważ będziemy otwierać i zamykać to okno wielokrotnie, powinniśmy umieścić logikę w metodzie, aby móc ją po prostu wywołać w razie potrzeby:
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; }); }
Kluczowe punkty:
- Będziemy musieli ustawić właściwość show na false w obiekcie options konstruktora BrowserWindow, aby zapobiec domyślnemu otwieraniu okna podczas uruchamiania aplikacji.
- Będziemy musieli zniszczyć instancję BrowserWindow za każdym razem, gdy okno uruchamia zdarzenie zamknięte .
Otwieranie i zamykanie okna „Wstaw”
Chodzi o to, aby móc uruchomić okno „wstaw”, gdy użytkownik końcowy kliknie przycisk w oknie „głównym”. Aby to zrobić, będziemy musieli wysłać wiadomość z głównego okna do głównego procesu, aby poinstruować go, aby otworzył okno wstawiania. Możemy to osiągnąć za pomocą modułu IPC firmy Electron. W rzeczywistości istnieją dwa warianty modułu IPC:
- Jeden dla procesu głównego, umożliwiający aplikacji subskrybowanie wiadomości wysyłanych z systemu Windows.
- Jeden dla procesu renderowania, który umożliwia aplikacji wysyłanie wiadomości do procesu głównego.
Chociaż kanał komunikacyjny Electrona jest w większości jednokierunkowy, możliwy jest dostęp do modułu IPC głównego procesu w procesie renderowania, korzystając z modułu zdalnego. Ponadto proces główny może wysłać komunikat z powrotem do procesu renderującego, z którego pochodzi zdarzenie, za pomocą metody Event.sender.send().
Aby użyć modułu IPC, potrzebujemy go jak każdego innego modułu NPM w naszym skrypcie procesu głównego:
var ipc = require('ipc');
… a następnie powiąż ze zdarzeniami metodą on()
:
ipc.on('toggle-insert-view', function() { if(!insertWindow) { createInsertWindow(); } return (!insertWindow.isClosed() && insertWindow.isVisible()) ? insertWindow.hide() : insertWindow.show(); });
Kluczowe punkty:
- Możemy nazwać wydarzenie w dowolny sposób, przykład jest po prostu dowolny.
- Nie zapomnij sprawdzić, czy instancja BrowserWindow jest już utworzona, jeśli nie, stwórz jej instancję.
- Instancja BrowserWindow ma kilka przydatnych metod:
- isClosed() zwraca wartość logiczną, niezależnie od tego, czy okno jest aktualnie
closed
. - isVisible() : zwraca wartość logiczną, niezależnie od tego, czy okno jest aktualnie widoczne.
- show() / hide() : wygodne metody pokazywania i ukrywania okna.
- isClosed() zwraca wartość logiczną, niezależnie od tego, czy okno jest aktualnie
Teraz musimy uruchomić to zdarzenie z procesu renderowania. Stworzymy nowy plik skryptu o nazwie main.view.js
i dodamy go do naszej strony HTML, tak jak w przypadku zwykłego skryptu:
<script src="./main.view.js"></script>
Załadowanie pliku skryptu za pomocą tagu HTML
script
powoduje wczytanie tego pliku w kontekście po stronie klienta. Oznacza to, że na przykład zmienne globalne są dostępne poprzezwindow.<varname>
. Aby załadować skrypt w kontekście po stronie serwera, możemy użyć metodyrequire()
bezpośrednio na naszej stronie HTML:require('./main.controller.js');
.
Mimo że skrypt jest ładowany w kontekście po stronie klienta , nadal możemy uzyskać dostęp do modułu IPC dla procesu renderującego w taki sam sposób, jak w przypadku procesu głównego, a następnie wysłać nasze zdarzenie jako takie:
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'); }); }; });
Dostępna jest również metoda sendSync() na wypadek, gdybyśmy musieli wysyłać nasze zdarzenia synchronicznie.
Teraz jedyne, co pozostało nam do zrobienia, aby otworzyć okno „wstaw”, to utworzyć przycisk HTML z pasującą dyrektywą Angular:
<div ng-controller="MainCtrl as vm"> <button toggle-insert-view class="mdl-button"> <i class="material-icons">add</i> </button> </div>
I dodaj tę dyrektywę jako zależność kontrolera Angular okna głównego:
angular .module('MainWindow', ['Utils']) .controller('MainCtrl', function() { var vm = this; });
Generowanie haseł
Aby uprościć sprawę, możemy po prostu użyć modułu NPM uuid
do wygenerowania unikalnych identyfikatorów, które będą działać jako hasła na potrzeby tego samouczka. Możemy go zainstalować jak każdy inny moduł NPM, wymagać go w naszym skrypcie „Utils”, a następnie stworzyć prostą fabrykę, która zwróci unikalny identyfikator:
var uuid = require('uuid'); angular .module('Utils', []) ... .factory('Generator', function() { return { create: function() { return uuid.v4(); } }; })
Teraz pozostaje nam tylko utworzyć przycisk w widoku wstawiania i dołączyć do niego dyrektywę, która będzie nasłuchiwać zdarzeń kliknięcia przycisku i wywołać metodę create():
<!-- 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(); }); }; }])
Zapisywanie haseł
W tym momencie chcemy przechowywać nasze hasła. Struktura danych dla naszych wpisów haseł jest dość prosta:
{ "id": String "description": String, "username": String, "password": String }
Tak więc wszystko, czego naprawdę potrzebujemy, to rodzaj bazy danych w pamięci, która może opcjonalnie zsynchronizować się z plikiem w celu wykonania kopii zapasowej. W tym celu Loki.js wydaje się idealnym kandydatem. Robi dokładnie to, czego potrzebujemy do celów tej aplikacji, a ponadto oferuje funkcję widoków dynamicznych , pozwalającą nam robić rzeczy podobne do modułu agregacji MongoDB.
Widoki dynamiczne nie oferują wszystkich funkcji dostępnych w module agregacji MongodDB. Więcej informacji można znaleźć w dokumentacji.
Zacznijmy od stworzenia prostego formularza HTML:
<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>
A teraz dodajmy logikę JavaScript do obsługi wysyłania i zapisywania zawartości formularza:

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'); }); } }); }; }])
Kluczowe punkty:
- Najpierw musimy zainicjować bazę danych. Proces ten obejmuje utworzenie nowej instancji obiektu Loki, podanie ścieżki do pliku bazy danych jako argumentu, sprawdzenie, czy plik kopii zapasowej istnieje, utworzenie go w razie potrzeby (w tym kolekcji „Pęk kluczy”), a następnie załadowanie zawartości ten plik w pamięci.
- Możemy pobrać konkretną kolekcję w bazie danych za pomocą metody
getCollection()
. - Obiekt kolekcji udostępnia kilka metod, w tym metodę
insert()
, umożliwiającą dodanie nowego dokumentu do kolekcji. - Aby zachować zawartość bazy danych w pliku, obiekt Loki udostępnia
saveDatabase()
. - Będziemy musieli zresetować dane formularza i wysłać zdarzenie IPC, aby nakazać głównemu procesowi zamknięcie okna po zapisaniu dokumentu.
Mamy teraz prosty formularz umożliwiający generowanie i zapisywanie nowych haseł. Wróćmy do głównego widoku, aby wyświetlić te wpisy.
Hasła aukcji
Kilka rzeczy musi się tutaj wydarzyć:
- Musimy być w stanie zdobyć wszystkie dokumenty z naszej kolekcji.
- Musimy informować główny widok o każdym zapisaniu nowego hasła, aby mógł odświeżyć widok.
Listę dokumentów możemy pobrać, wywołując getCollection()
na obiekcie Loki. Ta metoda zwraca obiekt z właściwością o nazwie data , która jest po prostu tablicą wszystkich dokumentów w tej kolekcji:
this.getCollection = function() { this.collection = this.db.getCollection('keychain'); return this.collection; }; this.getDocs = function() { return (this.getCollection()) ? this.getCollection().data : null; };
Możemy wtedy wywołać getDocs() w naszym kontrolerze Angular i pobrać wszystkie hasła przechowywane w bazie danych, po jej zainicjowaniu:
angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); }); });
Trochę szablonowania Angulara i mamy listę haseł:
<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]">•</span> </td> <td> <a href="#" copy-password="{{$index}}">copy</a> <a href="#" remove-password="{{item}}">remove</a> </td> </tr>
Przyjemną dodaną funkcją byłoby odświeżenie listy haseł po wstawieniu nowego. W tym celu możemy użyć modułu IPC firmy Electron. Jak wspomniano wcześniej, moduł IPC głównego procesu można wywołać w procesie renderującym, aby przekształcić go w proces nasłuchiwania za pomocą modułu zdalnego. Oto przykład jak to zaimplementować w 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(); }); }); }); }]);
Kluczowe punkty:
- Będziemy musieli użyć zdalnego modułu za pomocą jego własnej metody
require()
, aby zażądać zdalnego modułu IPC z procesu głównego. - Następnie możemy skonfigurować nasz proces renderowania jako detektor zdarzeń za pomocą metody
on()
i powiązać funkcje wywołań zwrotnych z tymi zdarzeniami.
Widok wstawiania będzie wtedy odpowiedzialny za wysyłanie tego zdarzenia za każdym razem, gdy zostanie zapisany nowy dokument:
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'); });
Kopiowanie haseł
Zazwyczaj wyświetlanie haseł w postaci zwykłego tekstu nie jest dobrym pomysłem. Zamiast tego zamierzamy ukryć i udostępnić wygodny przycisk umożliwiający użytkownikowi końcowemu skopiowanie hasła bezpośrednio do określonego wpisu.
Tutaj ponownie Electron przychodzi nam na ratunek, dostarczając nam moduł schowka z łatwymi metodami kopiowania i wklejania nie tylko treści tekstowych, ale także obrazów i kodu HTML:
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); }); }; }]);
Ponieważ wygenerowane hasło będzie prostym ciągiem znaków, możemy użyć metody writeText()
do skopiowania hasła do schowka systemowego. Następnie możemy zaktualizować kod HTML naszego głównego widoku i dodać przycisk kopiowania z dyrektywą copy-password
, dostarczając indeks tablicy haseł:
<a href="#" copy-password="{{$index}}">copy</a>
Usuwanie haseł
Nasi użytkownicy końcowi mogą również chcieć mieć możliwość usuwania haseł, na wypadek gdyby stały się przestarzałe. Aby to zrobić, wystarczy wywołać metodę remove()
w kolekcji pęku kluczy. Musimy dostarczyć cały dokument do metody 'remove()' w następujący sposób:
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); };
Dokumentacja Loki.js stwierdza, że możemy również usunąć dokument według jego identyfikatora, ale wygląda na to, że nie działa on zgodnie z oczekiwaniami.
Tworzenie menu pulpitu
Electron bezproblemowo integruje się z naszym środowiskiem graficznym systemu operacyjnego, aby zapewnić „natywny” wygląd i działanie naszych aplikacji. Dlatego Electron jest dostarczany z modułem Menu, przeznaczonym do tworzenia złożonych struktur menu pulpitu dla naszej aplikacji.
Moduł menu to obszerny temat i prawie zasługuje na samouczek. Gorąco polecam zapoznanie się z samouczkiem Electron Desktop Environment Integration, aby odkryć wszystkie funkcje tego modułu.
W zakresie tego bieżącego samouczka zobaczymy, jak utworzyć niestandardowe menu, dodać do niego niestandardowe polecenie i zaimplementować standardowe polecenie quit.
Tworzenie i przypisywanie niestandardowego menu do naszej aplikacji
Zazwyczaj logika JavaScript dla menu Electron należy do głównego pliku skryptu naszej aplikacji, gdzie jest zdefiniowany nasz proces główny. Możemy jednak wyodrębnić go do osobnego pliku i uzyskać dostęp do modułu Menu za pośrednictwem modułu zdalnego:
var remote = require('remote'), Menu = remote.require('menu');
Aby zdefiniować proste menu, będziemy musieli użyć metody buildFromTemplate()
:
var appMenu = Menu.buildFromTemplate([ { label: 'Electron', submenu: [{ label: 'Credits', click: function() { alert('Built with Electron & Loki.js.'); } }] } ]);
Pierwsza pozycja w tablicy jest zawsze używana jako „domyślna” pozycja menu.
Wartość właściwości
label
nie ma większego znaczenia dla domyślnego elementu menu. W trybie deweloperskim zawsze wyświetlaElectron
. Zobaczymy później, jak przypisać niestandardową nazwę do domyślnego elementu menu podczas fazy budowania.
Na koniec musimy przypisać to niestandardowe menu jako domyślne menu dla naszej aplikacji za pomocą metody setApplicationMenu()
:
Menu.setApplicationMenu(appMenu);
Mapowanie skrótów klawiaturowych
Electron zapewnia „akceleratory”, zestaw wstępnie zdefiniowanych ciągów, które są mapowane na rzeczywiste kombinacje klawiszy, np.: Command+A
lub Ctrl+Shift+Z
.
Akcelerator
Command
nie działa w systemie Windows ani Linux. W przypadku naszej aplikacji pęku kluczy haseł powinniśmy dodać pozycję menuFile
, oferującą dwa polecenia:
- Utwórz hasło : otwórz widok wstawiania za pomocą Cmd (lub Ctrl) + N
- Zakończ : zamknij aplikację całkowicie za pomocą Cmd (lub Ctrl) + 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!!! } ] } ...
Kluczowe punkty:
- Możemy dodać separator wizualny, dodając do tablicy element z właściwością
type
ustawioną naseparator
. - Akcelerator
CmdOrCtrl
jest kompatybilny zarówno z klawiaturami Mac, jak i PC - Właściwość
selector
jest zgodna tylko z OSX!
Stylizacja naszej aplikacji
Prawdopodobnie zauważyłeś w różnych przykładach kodu odniesienia do nazw klas zaczynających się od mdl-
. Na potrzeby tego samouczka zdecydowałem się użyć struktury interfejsu użytkownika Material Design Lite, ale możesz swobodnie korzystać z dowolnej wybranej struktury interfejsu użytkownika.
Wszystko, co możemy zrobić z HTML5, można zrobić w Electronie; pamiętaj tylko o rosnącym rozmiarze plików binarnych aplikacji i wynikających z tego problemach z wydajnością, które mogą wystąpić, jeśli używasz zbyt wielu bibliotek innych firm.
Pakowanie aplikacji elektronowych do dystrybucji
Stworzyłeś aplikację Electron, która wygląda świetnie, napisałeś swoje testy e2e za pomocą Selenium i WebDriver i jesteś gotowy do rozpowszechniania jej na całym świecie!
Ale nadal chcesz go spersonalizować, nadać mu niestandardową nazwę inną niż domyślna "Elektron", a być może także dostarczyć niestandardowe ikony aplikacji dla platform Mac i PC.
Budowanie z Gulp
W dzisiejszych czasach istnieje wtyczka Gulp do wszystkiego, co możemy wymyślić. Wszystko, co musiałem zrobić, to wpisać gulp electron
w Google i na pewno jest wtyczka gulp-electron!
Ta wtyczka jest dość łatwa w użyciu, o ile zachowano strukturę folderów opisaną na początku tego samouczka. Jeśli nie, być może będziesz musiał trochę przesunąć rzeczy.
Wtyczkę tę można zainstalować jak każdą inną wtyczkę Gulp:
$ npm install gulp-electron --save-dev
A potem możemy zdefiniować nasze zadanie Gulp jako takie:
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("")); });
Kluczowe punkty:
- folder
src/
nie może być taki sam, jak folder, w którym znajduje się plik Gulpfile.js, ani ten sam folder, co folder dystrybucyjny. - Możemy zdefiniować platformy, na które chcemy eksportować za pomocą tablicy
platforms
. - Powinniśmy zdefiniować folder
cache
, w którym będą pobierane pliki binarne Electron, aby można je było spakować za pomocą naszej aplikacji. - Zawartość pliku package.json aplikacji należy przekazać do zadania gulp za pomocą właściwości
packageJson
. - Dostępna jest opcjonalna właściwość
packaging
, która pozwala nam również tworzyć archiwa zip wygenerowanych aplikacji. - Dla każdej platformy istnieje inny zestaw „zasobów platformy”, które można zdefiniować.
Dodawanie ikon aplikacji
Jedną z właściwości platformResources
jest właściwość icon
, która pozwala nam zdefiniować niestandardową ikonę dla naszej aplikacji:
"icon": "keychain.ico"
OS X wymaga ikon z rozszerzeniem pliku
.icns
. Istnieje wiele narzędzi online, które pozwalają nam bezpłatnie konwertować pliki.png
na.ico
i.icns
.
Wniosek
W tym artykule tylko zarysowaliśmy powierzchnię tego, co tak naprawdę potrafi zrobić Electron. Pomyśl o świetnych aplikacjach, takich jak Atom lub Slack, jako o źródle inspiracji, do którego możesz skorzystać z tego narzędzia.
Mam nadzieję, że ten samouczek okazał się przydatny. Zachęcamy do pozostawienia komentarzy i podzielenia się swoimi doświadczeniami z Electron!