Electron: aplicaciones de escritorio multiplataforma simplificadas

Publicado: 2022-03-11

A principios de este año, Github lanzó Atom-Shell, el núcleo de su famoso editor de código abierto Atom, y lo renombró a Electron para la ocasión especial.

Electron, a diferencia de otros competidores en la categoría de aplicaciones de escritorio basadas en Node.js, aporta su propio giro a este mercado ya bien establecido al combinar el poder de Node.js (io.js hasta versiones recientes) con Chromium Engine para traer nosotros lo mejor de JavaScript del lado del cliente y del servidor.

Imagine un mundo en el que pudiéramos crear aplicaciones de escritorio multiplataforma basadas en datos y de alto rendimiento impulsadas no solo por el repositorio cada vez mayor de módulos NPM, sino también por todo el registro de Bower para satisfacer todas nuestras necesidades del lado del cliente.

Entra Electron.

Creación de aplicaciones de escritorio multiplataforma con Electron

Creación de aplicaciones de escritorio multiplataforma con Electron
Pío

En este tutorial, crearemos una aplicación de llavero de contraseña simple usando Electron, Angular.js y Loki.js, una base de datos liviana y en memoria con una sintaxis familiar para los desarrolladores de MongoDB.

El código fuente completo de esta aplicación está disponible aquí.

Este tutorial asume que:

  • El lector tiene Node.js y Bower instalados en su máquina.
  • Están familiarizados con la sintaxis de consulta similar a Node.js, Angular.js y MongoDB.

Conseguir los bienes

Lo primero es lo primero, necesitaremos obtener los binarios de Electron para probar nuestra aplicación localmente. Podemos instalarlo globalmente y usarlo como CLI, o instalarlo localmente en la ruta de nuestra aplicación. Recomiendo instalarlo globalmente, para que así no tengamos que hacerlo una y otra vez para cada aplicación que desarrollemos.

Más adelante aprenderemos cómo empaquetar nuestra aplicación para su distribución usando Gulp. Este proceso implica copiar los binarios de Electron y, por lo tanto, tiene poco o ningún sentido instalarlo manualmente en la ruta de nuestra aplicación.

Para instalar la CLI de Electron, podemos teclear el siguiente comando en nuestra terminal:

 $ npm install -g electron-prebuilt

Para probar la instalación, escriba electron -h y debería mostrar la versión de Electron CLI.

En el momento en que se escribió este artículo, la versión de Electron era 0.31.2 .

Configuración del proyecto

Supongamos la siguiente estructura básica de carpetas:

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

… donde: - cache/ se usará para descargar los archivos binarios de Electron cuando se construya la aplicación. - dist/ contendrá los archivos de distribución generados. - src/ contendrá nuestro código fuente. - src/app.js será el punto de entrada de nuestra aplicación.

A continuación, navegaremos a la carpeta src/ en nuestra terminal y crearemos los archivos package.json y bower.json para nuestra aplicación:

 $ npm init $ bower init

Instalaremos los paquetes necesarios más adelante en este tutorial.

Comprender los procesos de electrones

Electron distingue entre dos tipos de procesos:

  • El proceso principal : el punto de entrada de nuestra aplicación, el archivo que se ejecutará cada vez que ejecutemos la aplicación. Por lo general, este archivo declara las diversas ventanas de la aplicación y, opcionalmente, se puede usar para definir detectores de eventos globales mediante el módulo IPC de Electron.
  • El proceso Renderer : el controlador para una ventana dada en nuestra aplicación. Cada ventana crea su propio Proceso Renderizador.

Para mayor claridad del código, se debe usar un archivo separado para cada proceso de renderizado. Para definir el proceso principal de nuestra aplicación, src/app.js e incluiremos el módulo de la app para iniciar la aplicación y el módulo de la browser-window para crear las distintas ventanas de nuestra aplicación (ambas forman parte del núcleo de Electron), como tal:

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

Cuando la aplicación realmente se inicia, activa un evento ready , al que podemos vincularnos. En este punto, podemos instanciar la ventana principal de nuestra aplicación:

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

Puntos clave:

  • Creamos una nueva ventana creando una nueva instancia del objeto BrowserWindow .
  • Toma un objeto como único argumento, lo que nos permite definir varias configuraciones, entre las que se encuentran el ancho y el alto predeterminados de la ventana.
  • La instancia de la ventana tiene un método loadUrl() , que nos permite cargar el contenido de un archivo HTML real en la ventana actual. El archivo HTML puede ser local o remoto .
  • La instancia de la ventana tiene un método openDevTools() opcional, lo que nos permite abrir una instancia de Chrome Dev Tools en la ventana actual para fines de depuración.

A continuación, debemos organizar un poco nuestro código. Recomiendo crear una carpeta windows/ en nuestra carpeta src/ , y donde podemos crear una subcarpeta para cada ventana, así:

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

… donde main.controller.js contendrá la lógica del “lado del servidor” de nuestra aplicación, y main.view.js contendrá la lógica del “lado del cliente” de nuestra aplicación.

El archivo main.html es simplemente una página web HTML5, por lo que simplemente podemos iniciarlo así:

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

En este punto, nuestra aplicación debería estar lista para ejecutarse. Para probarlo, simplemente podemos escribir lo siguiente en nuestra terminal, en la raíz de la carpeta src :

 $ electron .

Podemos automatizar este proceso definiendo el script de start del archivo package.son.

Creación de una aplicación de escritorio de llavero con contraseña

Para crear una aplicación de llavero de contraseñas, necesitamos: - Una forma de agregar, generar y guardar contraseñas. - Una forma conveniente de copiar y eliminar contraseñas.

Generación y almacenamiento de contraseñas

Un simple formulario será suficiente para insertar nuevas contraseñas. Para demostrar la comunicación entre múltiples ventanas en Electron, comience agregando una segunda ventana en nuestra aplicación, que mostrará el formulario de "inserción". Dado que abriremos y cerraremos esta ventana varias veces, debemos envolver la lógica en un método para que podamos simplemente llamarlo cuando sea necesario:

 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; }); }

Puntos clave:

  • Tendremos que establecer la propiedad show en falso en el objeto de opciones del constructor de BrowserWindow, para evitar que la ventana se abra de forma predeterminada cuando se inician las aplicaciones.
  • Tendremos que destruir la instancia de BrowserWindow cada vez que la ventana active un evento cerrado .

Abrir y cerrar la ventana "Insertar"

La idea es poder activar la ventana "insertar" cuando el usuario final haga clic en un botón en la ventana "principal". Para hacer esto, necesitaremos enviar un mensaje desde la ventana principal al Proceso principal para indicarle que abra la ventana de inserción. Podemos lograr esto usando el módulo IPC de Electron. En realidad, hay dos variantes del módulo IPC:

  • Uno para el proceso principal, que permite que la aplicación se suscriba a los mensajes enviados desde Windows.
  • Uno para el proceso Renderer, que permite que la aplicación envíe mensajes al proceso principal.

Aunque el canal de comunicación de Electron es mayormente unidireccional, es posible acceder al módulo IPC del Proceso Principal en un Proceso de Representación haciendo uso del módulo remoto. Además, el proceso principal puede enviar un mensaje de vuelta al proceso de representación desde el que se originó el evento utilizando el método Event.sender.send().

Para usar el módulo IPC, solo lo requerimos como cualquier otro módulo NPM en nuestro script de proceso principal:

 var ipc = require('ipc');

… y luego enlazar eventos con el método on() :

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

Puntos clave:

  • Podemos nombrar el evento como queramos, el ejemplo es arbitrario.
  • No olvide verificar si la instancia de BrowserWindow ya está creada, si no, ejecútela.
  • La instancia de BrowserWindow tiene algunos métodos útiles:
    • isClosed() devuelve un valor booleano, ya sea que la ventana se encuentre actualmente en un estado closed o no.
    • isVisible() : devuelve un valor booleano, ya sea que la ventana esté visible o no.
    • show() / hide() : métodos convenientes para mostrar y ocultar la ventana.

Ahora necesitamos disparar ese evento desde el Proceso Renderer. Crearemos un nuevo archivo de secuencia de comandos llamado main.view.js y lo agregaremos a nuestra página HTML como lo haríamos con cualquier secuencia de comandos normal:

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

Al cargar el archivo de secuencia de comandos a través de la etiqueta de secuencia de script HTML, se carga este archivo en un contexto del lado del cliente. Esto significa que, por ejemplo, las variables globales están disponibles a través window.<varname> . Para cargar un script en un contexto del lado del servidor, podemos usar el método require() directamente en nuestra página HTML: require('./main.controller.js'); .

Aunque la secuencia de comandos se carga en el contexto del lado del cliente , aún podemos acceder al módulo IPC para el proceso Renderer de la misma manera que podemos hacerlo para el proceso principal y luego enviar nuestro evento como tal:

 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'); }); }; });

También hay un método sendSync() disponible, en caso de que necesitemos enviar nuestros eventos de forma sincrónica.

Ahora, todo lo que nos queda por hacer para abrir la ventana "insertar" es crear un botón HTML con la directiva Angular correspondiente:

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

Y agregue esa directiva como una dependencia del controlador Angular de la ventana principal:

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

Generación de contraseñas

Para simplificar las cosas, podemos usar el módulo uuid de NPM para generar ID únicos que actuarán como contraseñas para los fines de este tutorial. Podemos instalarlo como cualquier otro módulo NPM, requerirlo en nuestro script 'Utils' y luego crear una fábrica simple que devolverá una ID única:

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

Ahora, todo lo que nos queda por hacer es crear un botón en la vista de inserción y adjuntarle una directiva que escuchará los eventos de clic en el botón y llamará al método 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(); }); }; }])

Guardar contraseñas

En este punto, queremos almacenar nuestras contraseñas. La estructura de datos para nuestras entradas de contraseña es bastante simple:

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

Entonces, todo lo que realmente necesitamos es algún tipo de base de datos en memoria que, opcionalmente, pueda sincronizarse con el archivo para realizar una copia de seguridad. Para este propósito, Loki.js parece el candidato ideal. Hace exactamente lo que necesitamos para el propósito de esta aplicación, y además ofrece la función Vistas dinámicas , lo que nos permite hacer cosas similares al módulo de agregación de MongoDB.

Las vistas dinámicas no ofrecen toda la funcionalidad que ofrece el módulo de agregación de MongodDB. Consulte la documentación para obtener más información.

Comencemos por crear un formulario HTML simple:

 <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>

Y ahora, agreguemos la lógica de JavaScript para manejar la publicación y el guardado del contenido del formulario:

 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'); }); } }); }; }])

Puntos clave:

  • Primero necesitamos inicializar la base de datos. Este proceso implica crear una nueva instancia del Objeto Loki, proporcionar la ruta al archivo de la base de datos como argumento, buscar si existe ese archivo de respaldo, crearlo si es necesario (incluida la colección 'Keychain') y luego cargar el contenido de este archivo en la memoria.
  • Podemos recuperar una colección específica en la base de datos con el método getCollection() .
  • Un objeto de colección expone varios métodos, incluido un método insert() , que nos permite agregar un nuevo documento a la colección.
  • Para conservar el contenido de la base de datos en el archivo, el objeto Loki expone un método saveDatabase() .
  • Tendremos que restablecer los datos del formulario y enviar un evento IPC para indicarle al proceso principal que cierre la ventana una vez que se guarde el documento.

Ahora tenemos un formulario simple que nos permite generar y guardar nuevas contraseñas. Volvamos a la vista principal para enumerar estas entradas.

Listado de contraseñas

Algunas cosas deben suceder aquí:

  • Necesitamos poder obtener todos los documentos de nuestra colección.
  • Necesitamos informar a la vista principal cada vez que se guarda una nueva contraseña para que pueda actualizar la vista.

Podemos recuperar la lista de documentos llamando al método getCollection() en el objeto Loki. Este método devuelve un objeto con una propiedad llamada data , que es simplemente una matriz de todos los documentos de esa colección:

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

Luego podemos llamar a getDocs() en nuestro controlador Angular y recuperar todas las contraseñas almacenadas en la base de datos, después de inicializarlo:

 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 poco de plantilla Angular, y tenemos una lista de contraseñas:

 <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> 

Una buena característica adicional sería actualizar la lista de contraseñas después de insertar una nueva. Para ello, podemos utilizar el módulo IPC de Electron. Como se mencionó anteriormente, el módulo IPC del proceso principal se puede llamar en un proceso de representación para convertirlo en un proceso de escucha, utilizando el módulo remoto. Aquí hay un ejemplo de cómo implementarlo en 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(); }); }); }); }]);

Puntos clave:

  • Tendremos que usar el módulo remoto a través de su propio método require() para solicitar el módulo IPC remoto del proceso principal.
  • A continuación, podemos configurar nuestro proceso Renderer como detector de eventos a través del método on() y vincular las funciones de devolución de llamada a estos eventos.

La vista de inserción se encargará de enviar este evento cada vez que se guarde un nuevo 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'); });

Copia de contraseñas

Por lo general, no es una buena idea mostrar las contraseñas en texto sin formato. En su lugar, vamos a ocultar y proporcionar un botón de conveniencia que le permita al usuario final copiar la contraseña directamente para una entrada específica.

Aquí nuevamente, Electron viene a nuestro rescate brindándonos un módulo de portapapeles con métodos fáciles para copiar y pegar no solo contenido de texto, sino también imágenes y código 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); }); }; }]);

Dado que la contraseña generada será una cadena simple, podemos usar el método writeText() para copiar la contraseña en el portapapeles del sistema. Luego podemos actualizar nuestra vista principal HTML y agregar el botón de copia con la directiva copy-password , proporcionando el índice de la matriz de contraseñas:

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

Eliminación de contraseñas

A nuestros usuarios finales también les gustaría poder eliminar las contraseñas, en caso de que se vuelvan obsoletas. Para hacer esto, todo lo que tenemos que hacer es llamar al método remove() en la colección de llaveros. Necesitamos proporcionar el documento completo al método 'remove()', como tal:

 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 documentación de Loki.js indica que también podemos eliminar un documento por su ID, pero parece que no funciona como se esperaba.

Creación de un menú de escritorio

Electron se integra a la perfección con el entorno de escritorio de nuestro sistema operativo para brindar una experiencia de usuario "nativa" a nuestras aplicaciones. Por lo tanto, Electron viene con un módulo de Menú, dedicado a crear estructuras de menú de escritorio complejas para nuestra aplicación.

El módulo de menú es un tema amplio y casi merece un tutorial propio. Le recomiendo que lea el tutorial de integración del entorno de escritorio de Electron para descubrir todas las funciones de este módulo.

Para el alcance de este tutorial actual, veremos cómo crear un menú personalizado, agregarle un comando personalizado e implementar el comando de salida estándar.

Crear y asignar un menú personalizado a nuestra aplicación

Por lo general, la lógica de JavaScript para un menú de Electron pertenecería al archivo de script principal de nuestra aplicación, donde se define nuestro Proceso principal. Sin embargo, podemos abstraerlo en un archivo separado y acceder al módulo Menú a través del módulo remoto:

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

Para definir un menú simple, necesitaremos usar el método buildFromTemplate() :

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

El primer elemento de la matriz siempre se usa como el elemento de menú "predeterminado".

El valor de la propiedad de la label no importa mucho para el elemento de menú predeterminado. En el modo de desarrollo, siempre mostrará Electron . Más adelante veremos cómo asignar un nombre personalizado al elemento de menú predeterminado durante la fase de construcción.

Finalmente, debemos asignar este menú personalizado como menú predeterminado para nuestra aplicación con el método setApplicationMenu() :

 Menu.setApplicationMenu(appMenu);

Asignación de atajos de teclado

Electron proporciona "aceleradores", un conjunto de cadenas predefinidas que se asignan a combinaciones de teclado reales, por ejemplo: Command+A o Ctrl+Shift+Z .

El acelerador Command no funciona en Windows o Linux. Para nuestra aplicación de llavero de contraseña, debemos agregar un elemento de menú File , ofreciendo dos comandos:

  • Crear contraseña : abra la vista de inserción con Cmd (o Ctrl) + N
  • Salir : salga de la aplicación por completo 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!!! } ] } ...

Puntos clave:

  • Podemos agregar un separador visual agregando un elemento a la matriz con la propiedad de type establecida en separator .
  • El acelerador CmdOrCtrl es compatible con teclados de Mac y PC
  • ¡La propiedad del selector solo es compatible con OSX!

Estilo de nuestra aplicación

Probablemente notó a lo largo de los diversos ejemplos de código referencias a nombres de clases que comienzan con mdl- . A los efectos de este tutorial, opté por utilizar el marco de interfaz de usuario de Material Design Lite, pero no dude en utilizar cualquier marco de interfaz de usuario de su elección.

Cualquier cosa que podamos hacer con HTML5 se puede hacer en Electron; solo tenga en cuenta el tamaño creciente de los binarios de la aplicación y los problemas de rendimiento resultantes que pueden ocurrir si usa demasiadas bibliotecas de terceros.

Empaquetado de aplicaciones electrónicas para distribución

Hiciste una aplicación Electron, se ve muy bien, escribiste tus pruebas e2e con Selenium y WebDriver, ¡y estás listo para distribuirla al mundo!

Pero aún desea personalizarlo, darle un nombre personalizado que no sea el "Electron" predeterminado, y tal vez también proporcionar íconos de aplicaciones personalizados para plataformas Mac y PC.

Construyendo con Gulp

En estos días, hay un complemento de Gulp para cualquier cosa que se nos ocurra. ¡Todo lo que tenía que hacer era escribir gulp electron en Google y, por supuesto, hay un complemento de trago de electrones!

Este complemento es bastante fácil de usar siempre que se mantenga la estructura de carpetas detallada al comienzo de este tutorial. Si no, es posible que tengas que mover las cosas un poco.

Este complemento se puede instalar como cualquier otro complemento de Gulp:

 $ npm install gulp-electron --save-dev

Y luego podemos definir nuestra tarea Gulp como tal:

 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("")); });

Puntos clave:

  • la carpeta src/ no puede ser la misma que la carpeta donde está Gulpfile.js, ni la misma carpeta que la carpeta de distribución.
  • Podemos definir las plataformas a las que deseamos exportar a través de la matriz de platforms .
  • Debemos definir una carpeta de cache , donde se descargarán los binarios de Electron para que puedan empaquetarse con nuestra aplicación.
  • El contenido del archivo package.json de la aplicación debe pasarse a la tarea gulp a través de la propiedad packageJson .
  • Hay una propiedad de packaging opcional, que nos permite crear también archivos zip de las aplicaciones generadas.
  • Para cada plataforma, hay un conjunto diferente de "recursos de plataforma" que se pueden definir.

Agregar iconos de aplicaciones

Una de las propiedades de platformResources es la propiedad icon , que nos permite definir un icono personalizado para nuestra aplicación:

 "icon": "keychain.ico"

OS X requiere iconos con la extensión de archivo .icns . Existen múltiples herramientas en línea que nos permiten convertir archivos .png en .ico e .icns de forma gratuita.

Conclusión

En este artículo solo hemos arañado la superficie de lo que Electron realmente puede hacer. Piense en grandes aplicaciones como Atom o Slack como una fuente de inspiración a la que puede acudir con esta herramienta.

Espero que hayas encontrado útil este tutorial, ¡no dudes en dejar tus comentarios y compartir tus experiencias con Electron!