Electron: le app desktop multipiattaforma semplificate
Pubblicato: 2022-03-11All'inizio di quest'anno, Github ha rilasciato Atom-Shell, il nucleo del suo famoso editor open source Atom, e lo ha ribattezzato Electron per l'occasione speciale.
Electron, a differenza di altri concorrenti nella categoria delle applicazioni desktop basate su Node.js, porta la sua svolta a questo mercato già consolidato combinando la potenza di Node.js (io.js fino ai recenti rilasci) con il Chromium Engine per portare noi il meglio di JavaScript lato server e client.
Immagina un mondo in cui potremmo creare applicazioni desktop performanti, basate sui dati e multipiattaforma alimentate non solo dal repository in continua crescita di moduli NPM, ma anche dall'intero registro Bower per soddisfare tutte le nostre esigenze lato client.
Entra Elettrone.
In questo tutorial creeremo una semplice applicazione portachiavi password utilizzando Electron, Angular.js e Loki.js, un database leggero e in memoria con una sintassi familiare per gli sviluppatori MongoDB.
Il codice sorgente completo per questa applicazione è disponibile qui.
Questo tutorial presuppone che:
- Il lettore ha Node.js e Bower installati sulla loro macchina.
- Hanno familiarità con la sintassi delle query di tipo Node.js, Angular.js e MongoDB.
Ottenere la merce
Per prima cosa, dovremo ottenere i binari di Electron per testare la nostra app localmente. Possiamo installarlo a livello globale e usarlo come CLI, oppure installarlo localmente nel percorso della nostra applicazione. Consiglio di installarlo a livello globale, in modo da non doverlo ripetere più e più volte per ogni app che sviluppiamo.
Impareremo più avanti come impacchettare la nostra applicazione per la distribuzione utilizzando Gulp. Questo processo comporta la copia dei binari di Electron e quindi ha poco o nessun senso installarlo manualmente nel percorso della nostra applicazione.
Per installare Electron CLI, possiamo digitare il seguente comando nel nostro terminale:
$ npm install -g electron-prebuilt
Per testare l'installazione, digitare electron -h
e dovrebbe visualizzare la versione di Electron CLI.
Al momento della stesura di questo articolo, la versione di Electron era 0.31.2
.
Allestimento del progetto
Assumiamo la seguente struttura di cartelle di base:
my-app |- cache/ |- dist/ |- src/ |-- app.js | gulpfile.js
… dove: - cache/ verrà utilizzato per scaricare i binari di Electron durante la creazione dell'app. - dist/ conterrà i file di distribuzione generati. - src/ conterrà il nostro codice sorgente. - src/app.js sarà il punto di ingresso della nostra applicazione.
Successivamente, passeremo alla cartella src/
nel nostro terminale e creeremo i file package.json
e bower.json
per la nostra app:
$ npm init $ bower init
Installeremo i pacchetti necessari più avanti in questo tutorial.
Comprendere i processi elettronici
Electron distingue tra due tipi di processi:
- Il processo principale : il punto di ingresso della nostra applicazione, il file che verrà eseguito ogni volta che eseguiamo l'app. In genere, questo file dichiara le varie finestre dell'app e può essere utilizzato facoltativamente per definire listener di eventi globali utilizzando il modulo IPC di Electron.
- Il processo di rendering : il controller per una determinata finestra nella nostra applicazione. Ogni finestra crea il proprio processo di rendering.
Per chiarezza del codice, è necessario utilizzare un file separato per ciascun processo di rendering. Per definire il processo principale per la nostra app, apriremo
src/app.js
e includeremo il moduloapp
per avviare l'app e il modulobrowser-window
per creare le varie finestre della nostra app (entrambe parte del core di Electron), come tale:
var app = require('app'), BrowserWindow = require('browser-window');
Quando l'app viene effettivamente avviata, genera un evento ready
, a cui possiamo associarci. A questo punto, possiamo istanziare la finestra principale della nostra app:
var mainWindow = null; app.on('ready', function() { mainWindow = new BrowserWindow({ width: 1024, height: 768 }); mainWindow.loadUrl('file://' + __dirname + '/windows/main/main.html'); mainWindow.openDevTools(); });
Punti chiave:
- Creiamo una nuova finestra creando una nuova istanza dell'oggetto
BrowserWindow
. - Prende un oggetto come argomento singolo, permettendoci di definire varie impostazioni, tra cui la larghezza e l' altezza di default della finestra.
- L'istanza della finestra ha un metodo
loadUrl()
, che ci consente di caricare il contenuto di un vero file HTML nella finestra corrente. Il file HTML può essere locale o remoto . - L'istanza della finestra ha un metodo
openDevTools()
opzionale, che ci consente di aprire un'istanza di Chrome Dev Tools nella finestra corrente per scopi di debug.
Successivamente, dovremmo organizzare un po' il nostro codice. Consiglio di creare una cartella windows/
nella nostra cartella src/
e dove possiamo creare una sottocartella per ogni finestra, in quanto tale:
my-app |- src/ |-- windows/ |--- main/ |---- main.controller.js |---- main.html |---- main.view.js
... dove main.controller.js
conterrà la logica "lato server" della nostra applicazione e main.view.js
conterrà la logica "lato client" della nostra applicazione.
Il file main.html
è semplicemente una pagina Web HTML5, quindi possiamo semplicemente avviarla in questo modo:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Password Keychain</title> </head> <body> <h1>Password Keychain</h1> </body> </html>
A questo punto, la nostra app dovrebbe essere pronta per essere eseguita. Per testarlo, possiamo semplicemente digitare quanto segue nel nostro terminale, nella radice della cartella src
:
$ electron .
Possiamo automatizzare questo processo definendo lo script di
start
del file package.son.
Creazione di un'app desktop per portachiavi con password
Per creare un'applicazione portachiavi password, abbiamo bisogno di: - Un modo per aggiungere, generare e salvare password. - Un modo conveniente per copiare e rimuovere le password.
Generazione e salvataggio di password
Sarà sufficiente un semplice modulo per inserire nuove password. Per dimostrare la comunicazione tra più finestre in Electron, inizia aggiungendo una seconda finestra nella nostra applicazione, che visualizzerà il modulo "inserisci". Poiché apriremo e chiuderemo questa finestra più volte, dovremmo racchiudere la logica in un metodo in modo da poterla semplicemente chiamare quando necessario:
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; }); }
Punti chiave:
- Dovremo impostare la proprietà show su false nell'oggetto options del costruttore BrowserWindow, per impedire che la finestra venga aperta per impostazione predefinita all'avvio delle applicazioni.
- Dovremo distruggere l'istanza BrowserWindow ogni volta che la finestra genera un evento chiuso .
Apertura e chiusura della finestra "Inserisci".
L'idea è quella di poter attivare la finestra "Inserisci" quando l'utente finale fa clic su un pulsante nella finestra "principale". Per fare ciò, dovremo inviare un messaggio dalla finestra principale al processo principale per istruirlo ad aprire la finestra di inserimento. Possiamo raggiungere questo obiettivo utilizzando il modulo IPC di Electron. Esistono in realtà due varianti del modulo IPC:
- Uno per il processo principale, che consente all'app di iscriversi ai messaggi inviati da Windows.
- Uno per il processo di rendering, che consente all'app di inviare messaggi al processo principale.
Sebbene il canale di comunicazione di Electron sia prevalentemente unidirezionale, è possibile accedere al modulo IPC del processo principale in un processo di rendering utilizzando il modulo remoto. Inoltre, il processo principale può inviare un messaggio al processo di rendering da cui ha avuto origine l'evento utilizzando il metodo Event.sender.send().
Per utilizzare il modulo IPC, lo richiediamo semplicemente come qualsiasi altro modulo NPM nel nostro script Main Process:
var ipc = require('ipc');
... e quindi associare agli eventi con il metodo on()
:
ipc.on('toggle-insert-view', function() { if(!insertWindow) { createInsertWindow(); } return (!insertWindow.isClosed() && insertWindow.isVisible()) ? insertWindow.hide() : insertWindow.show(); });
Punti chiave:
- Possiamo nominare l'evento come vogliamo, l'esempio è semplicemente arbitrario.
- Non dimenticare di controllare se l'istanza BrowserWindow è già stata creata, in caso contrario istanziala.
- L'istanza BrowserWindow ha alcuni metodi utili:
- isClosed() restituisce un valore booleano, indipendentemente dal fatto che la finestra sia attualmente
closed
o meno. - isVisible() : restituisce un valore booleano, indipendentemente dal fatto che la finestra sia attualmente visibile o meno.
- show() / hide() : metodi pratici per mostrare e nascondere la finestra.
- isClosed() restituisce un valore booleano, indipendentemente dal fatto che la finestra sia attualmente
Ora abbiamo effettivamente bisogno di attivare quell'evento dal processo di rendering. Creeremo un nuovo file di script chiamato main.view.js
e lo aggiungeremo alla nostra pagina HTML come faremmo con qualsiasi normale script:
<script src="./main.view.js"></script>
Il caricamento del file di script tramite il tag di
script
HTML carica questo file in un contesto lato client. Ciò significa che, ad esempio, le variabili globali sono disponibili tramitewindow.<varname>
. Per caricare uno script in un contesto lato server, possiamo usare il metodorequire()
direttamente nella nostra pagina HTML:require('./main.controller.js');
.
Anche se lo script è caricato nel contesto lato client , possiamo comunque accedere al modulo IPC per il processo di rendering nello stesso modo in cui possiamo per il processo principale, quindi inviare il nostro evento come tale:
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'); }); }; });
C'è anche un metodo sendSync() disponibile, nel caso abbiamo bisogno di inviare i nostri eventi in modo sincrono.
Ora, tutto ciò che ci resta da fare per aprire la finestra "insert" è creare un pulsante HTML con la direttiva Angular corrispondente su di esso:
<div ng-controller="MainCtrl as vm"> <button toggle-insert-view class="mdl-button"> <i class="material-icons">add</i> </button> </div>
E aggiungi quella direttiva come dipendenza del controller Angular della finestra principale:
angular .module('MainWindow', ['Utils']) .controller('MainCtrl', function() { var vm = this; });
Generazione di password
Per semplificare le cose, possiamo semplicemente utilizzare il modulo uuid
NPM per generare ID univoci che fungeranno da password ai fini di questo tutorial. Possiamo installarlo come qualsiasi altro modulo NPM, richiederlo nel nostro script "Utils" e quindi creare un semplice factory che restituirà un ID univoco:
var uuid = require('uuid'); angular .module('Utils', []) ... .factory('Generator', function() { return { create: function() { return uuid.v4(); } }; })
Ora, tutto ciò che resta da fare è creare un pulsante nella vista di inserimento e allegare ad esso una direttiva che ascolterà gli eventi di clic sul pulsante e chiamerà il metodo 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(); }); }; }])
Salvataggio delle password
A questo punto, vogliamo memorizzare le nostre password. La struttura dei dati per l'inserimento delle nostre password è abbastanza semplice:
{ "id": String "description": String, "username": String, "password": String }
Quindi tutto ciò di cui abbiamo veramente bisogno è una sorta di database in memoria che possa facoltativamente sincronizzarsi con il file per il backup. A questo scopo, Loki.js sembra il candidato ideale. Fa esattamente ciò di cui abbiamo bisogno per lo scopo di questa applicazione e offre in aggiunta la funzione Viste dinamiche , che ci consente di fare cose simili al modulo Aggregation di MongoDB.
Le viste dinamiche non offrono tutte le funzionalità offerte dal modulo Aggregation di MongodDB. Fare riferimento alla documentazione per ulteriori informazioni.
Iniziamo creando un semplice form 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>
E ora, aggiungiamo la logica JavaScript per gestire la pubblicazione e il salvataggio dei contenuti del modulo:

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'); }); } }); }; }])
Punti chiave:
- Dobbiamo prima inizializzare il database. Questo processo prevede la creazione di una nuova istanza dell'oggetto Loki, fornendo il percorso al file di database come argomento, cercando se il file di backup esiste, creandolo se necessario (inclusa la raccolta "Keychain") e quindi caricando il contenuto di questo file in memoria.
- Possiamo recuperare una collezione specifica nel database con il metodo
getCollection()
. - Un oggetto raccolta espone diversi metodi, incluso un metodo
insert()
, consentendoci di aggiungere un nuovo documento alla raccolta. - Per salvare in un file il contenuto del database, l'oggetto Loki espone un metodo
saveDatabase()
. - Dovremo reimpostare i dati del modulo e inviare un evento IPC per dire al processo principale di chiudere la finestra una volta che il documento è stato salvato.
Ora abbiamo un semplice modulo che ci consente di generare e salvare nuove password. Torniamo alla vista principale per elencare queste voci.
Elenco delle password
Qui devono succedere alcune cose:
- Dobbiamo essere in grado di ottenere tutti i documenti della nostra collezione.
- È necessario informare la vista principale ogni volta che viene salvata una nuova password in modo che possa aggiornare la vista.
Possiamo recuperare l'elenco dei documenti chiamando il metodo getCollection()
sull'oggetto Loki. Questo metodo restituisce un oggetto con una proprietà chiamata data , che è semplicemente un array di tutti i documenti in quella raccolta:
this.getCollection = function() { this.collection = this.db.getCollection('keychain'); return this.collection; }; this.getDocs = function() { return (this.getCollection()) ? this.getCollection().data : null; };
Possiamo quindi chiamare getDocs() nel nostro controller Angular e recuperare tutte le password memorizzate nel database, dopo averlo inizializzato:
angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); }); });
Un po' di modelli angolari e abbiamo un elenco di password:
<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>
Una bella funzionalità aggiunta sarebbe quella di aggiornare l'elenco delle password dopo averne inserito una nuova. Per questo, possiamo usare il modulo IPC di Electron. Come accennato in precedenza, il modulo IPC del processo principale può essere chiamato in un processo di rendering per trasformarlo in un processo listener, utilizzando il modulo remoto. Ecco un esempio su come implementarlo 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(); }); }); }); }]);
Punti chiave:
- Sarà necessario utilizzare il modulo remoto tramite il proprio metodo
require()
per richiedere il modulo IPC remoto dal processo principale. - Possiamo quindi impostare il nostro processo di rendering come listener di eventi tramite il metodo
on()
e associare le funzioni di callback a questi eventi.
La visualizzazione di inserimento sarà quindi responsabile dell'invio di questo evento ogni volta che viene salvato un nuovo documento:
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'); });
Copiare le password
Di solito non è una buona idea visualizzare le password in testo normale. Invece, nasconderemo e forniremo un comodo pulsante che consente all'utente finale di copiare direttamente la password per una voce specifica.
Anche in questo caso, Electron viene in nostro soccorso fornendoci un modulo appunti con metodi semplici per copiare e incollare non solo il contenuto del testo, ma anche le immagini e il codice 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); }); }; }]);
Poiché la password generata sarà una semplice stringa, possiamo usare il metodo writeText()
per copiare la password negli appunti del sistema. Possiamo quindi aggiornare la nostra visualizzazione principale HTML e aggiungere il pulsante di copia con la direttiva copy-password
, fornendo l'indice dell'array di password:
<a href="#" copy-password="{{$index}}">copy</a>
Rimozione delle password
I nostri utenti finali potrebbero anche voler eliminare le password, nel caso in cui diventino obsolete. Per fare ciò, tutto ciò che dobbiamo fare è chiamare il metodo remove()
sulla collezione di portachiavi. Dobbiamo fornire l'intero documento al metodo 'remove()', in quanto tale:
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); };
La documentazione di Loki.js afferma che possiamo anche rimuovere un documento tramite il suo ID, ma non sembra funzionare come previsto.
Creazione di un menu desktop
Electron si integra perfettamente con il nostro ambiente desktop OS per fornire un'esperienza utente "nativa" alle nostre app. Pertanto, Electron viene fornito in bundle con un modulo Menu, dedicato alla creazione di complesse strutture di menu desktop per la nostra app.
Il modulo menu è un argomento vasto e merita quasi un tutorial a parte. Ti consiglio vivamente di leggere il tutorial sull'integrazione dell'ambiente desktop di Electron per scoprire tutte le funzionalità di questo modulo.
Per lo scopo di questo tutorial corrente, vedremo come creare un menu personalizzato, aggiungervi un comando personalizzato e implementare il comando esci standard.
Creazione e assegnazione di un menu personalizzato alla nostra app
In genere, la logica JavaScript per un menu Electron apparterrebbe al file di script principale della nostra app, dove è definito il nostro processo principale. Tuttavia, possiamo astrarlo in un file separato e accedere al modulo Menu tramite il modulo remoto:
var remote = require('remote'), Menu = remote.require('menu');
Per definire un menu semplice, dovremo utilizzare il metodo buildFromTemplate()
:
var appMenu = Menu.buildFromTemplate([ { label: 'Electron', submenu: [{ label: 'Credits', click: function() { alert('Built with Electron & Loki.js.'); } }] } ]);
La prima voce dell'array viene sempre utilizzata come voce di menu "predefinita".
Il valore della proprietà
label
non ha molta importanza per la voce di menu predefinita. In modalità dev visualizzerà sempreElectron
. Vedremo più avanti come assegnare un nome personalizzato alla voce di menu di default durante la fase di build.
Infine, dobbiamo assegnare questo menu personalizzato come menu predefinito per la nostra app con il metodo setApplicationMenu()
:
Menu.setApplicationMenu(appMenu);
Mappatura delle scorciatoie da tastiera
Electron fornisce "acceleratori", un insieme di stringhe predefinite che si associano alle effettive combinazioni di tastiera, ad esempio: Command+A
o Ctrl+Shift+Z
.
L'acceleratore
Command
non funziona su Windows o Linux. Per la nostra applicazione portachiavi password, dovremmo aggiungere una voce di menuFile
, che offre due comandi:
- Crea password : apri la vista di inserimento con Cmd (o Ctrl) + N
- Esci : esci del tutto dall'app con Cmd (o 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!!! } ] } ...
Punti chiave:
- Possiamo aggiungere un separatore visivo aggiungendo un elemento all'array con la proprietà
type
impostata suseparator
. - L'acceleratore
CmdOrCtrl
è compatibile con le tastiere Mac e PC - La proprietà del
selector
è compatibile solo con OSX!
Styling della nostra app
Probabilmente hai notato nei vari esempi di codice riferimenti a nomi di classi che iniziano con mdl-
. Ai fini di questo tutorial ho scelto di utilizzare il framework dell'interfaccia utente di Material Design Lite, ma sentiti libero di utilizzare qualsiasi framework dell'interfaccia utente di tua scelta.
Tutto ciò che possiamo fare con HTML5 può essere fatto in Electron; tieni solo a mente la dimensione crescente dei file binari dell'app e i conseguenti problemi di prestazioni che potrebbero verificarsi se utilizzi troppe librerie di terze parti.
Packaging di app elettroniche per la distribuzione
Hai creato un'app Electron, sembra fantastica, hai scritto i tuoi test e2e con Selenium e WebDriver e sei pronto per distribuirla al mondo!
Ma vuoi comunque personalizzarlo, dargli un nome personalizzato diverso da quello predefinito "Electron" e forse anche fornire icone di applicazioni personalizzate per piattaforme Mac e PC.
Costruire con Gulp
Al giorno d'oggi, c'è un plug-in Gulp per qualsiasi cosa ci venga in mente. Tutto quello che dovevo fare è digitare gulp electron
su Google, e sicuramente c'è un plug-in gulp-electron!
Questo plugin è abbastanza facile da usare fintanto che è stata mantenuta la struttura delle cartelle dettagliata all'inizio di questo tutorial. In caso contrario, potresti dover spostare un po' le cose.
Questo plugin può essere installato come qualsiasi altro plugin di Gulp:
$ npm install gulp-electron --save-dev
E quindi possiamo definire il nostro compito Gulp come tale:
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("")); });
Punti chiave:
- la cartella
src/
non può essere la stessa della cartella in cui si trova Gulpfile.js, né la stessa cartella della cartella di distribuzione. - Possiamo definire le piattaforme su cui desideriamo esportare tramite l'array delle
platforms
. - Dovremmo definire una cartella
cache
, dove verranno scaricati i binari di Electron in modo che possano essere impacchettati con la nostra app. - Il contenuto del file package.json dell'app deve essere passato all'attività gulp tramite la proprietà
packageJson
. - Esiste una proprietà di
packaging
opzionale, che ci consente di creare anche archivi zip delle app generate. - Per ogni piattaforma è possibile definire un diverso insieme di “risorse di piattaforma”.
Aggiunta di icone di app
Una delle proprietà platformResources
è la proprietà icon
, che ci consente di definire un'icona personalizzata per la nostra app:
"icon": "keychain.ico"
OS X richiede icone con estensione
.icns
. Esistono diversi strumenti online che ci consentono di convertire gratuitamente i file.png
in.ico
e.icns
.
Conclusione
In questo articolo abbiamo solo scalfito la superficie di ciò che Electron può effettivamente fare. Pensa a fantastiche app come Atom o Slack come una fonte di ispirazione in cui puoi andare con questo strumento.
Spero che tu abbia trovato utile questo tutorial, sentiti libero di lasciare i tuoi commenti e condividere le tue esperienze con Electron!