Electron: aplicativos de desktop multiplataforma facilitados
Publicados: 2022-03-11No início deste ano, o Github lançou o Atom-Shell, o núcleo de seu famoso editor de código aberto Atom, e o renomeou para Electron para a ocasião especial.
A Electron, ao contrário de outros concorrentes na categoria de aplicativos de desktop baseados em Node.js, traz seu próprio toque para este mercado já bem estabelecido, combinando o poder do Node.js (io.js até lançamentos recentes) com o Chromium Engine para trazer o melhor do JavaScript do lado do servidor e do cliente.
Imagine um mundo onde pudéssemos construir aplicativos de desktop multiplataforma, orientados a dados e de alto desempenho, alimentados não apenas pelo crescente repositório de módulos NPM, mas também por todo o registro Bower para atender a todas as nossas necessidades do lado do cliente.
Entra Elétron.
Neste tutorial, construiremos um aplicativo simples de chaveiro de senha usando Electron, Angular.js e Loki.js, um banco de dados leve e na memória com uma sintaxe familiar para desenvolvedores do MongoDB.
O código-fonte completo para este aplicativo está disponível aqui.
Este tutorial pressupõe que:
- O leitor tem Node.js e Bower instalados em sua máquina.
- Eles estão familiarizados com Node.js, Angular.js e sintaxe de consulta do tipo MongoDB.
Obtendo as mercadorias
Primeiramente, precisaremos obter os binários do Electron para testar nosso aplicativo localmente. Podemos instalá-lo globalmente e usá-lo como CLI, ou instalá-lo localmente no caminho do nosso aplicativo. Eu recomendo instalá-lo globalmente, para que não tenhamos que fazer isso repetidamente para cada aplicativo que desenvolvemos.
Aprenderemos mais tarde como empacotar nosso aplicativo para distribuição usando o Gulp. Esse processo envolve copiar os binários do Electron e, portanto, faz pouco ou nenhum sentido instalá-lo manualmente no caminho do nosso aplicativo.
Para instalar o Electron CLI, podemos digitar o seguinte comando em nosso terminal:
$ npm install -g electron-prebuilt
Para testar a instalação, digite electron -h
e deve exibir a versão do Electron CLI.
Na época em que este artigo foi escrito, a versão do Electron era 0.31.2
.
Configurando o Projeto
Vamos supor a seguinte estrutura básica de pastas:
my-app |- cache/ |- dist/ |- src/ |-- app.js | gulpfile.js
… onde: - cache/ será usado para baixar os binários do Electron ao construir o aplicativo. - dist/ conterá os arquivos de distribuição gerados. - src/ conterá nosso código-fonte. - src/app.js será o ponto de entrada da nossa aplicação.
Em seguida, navegaremos até a pasta src/
em nosso terminal e criaremos os arquivos package.json
e bower.json
para nosso aplicativo:
$ npm init $ bower init
Instalaremos os pacotes necessários mais adiante neste tutorial.
Entendendo os processos eletrônicos
Electron distingue entre dois tipos de processos:
- O Processo Principal : O ponto de entrada da nossa aplicação, o arquivo que será executado sempre que executarmos a aplicação. Normalmente, esse arquivo declara as várias janelas do aplicativo e, opcionalmente, pode ser usado para definir ouvintes de eventos globais usando o módulo IPC do Electron.
- The Renderer Process : O controlador de uma determinada janela em nosso aplicativo. Cada janela cria seu próprio processo de renderização.
Para maior clareza do código, um arquivo separado deve ser usado para cada processo de renderização. Para definir o processo principal para nosso aplicativo, vamos abrir
src/app.js
e incluir o móduloapp
para iniciar o aplicativo e o módulobrowser-window
para criar as várias janelas do nosso aplicativo (ambas parte do núcleo Electron), Como tal:
var app = require('app'), BrowserWindow = require('browser-window');
Quando o aplicativo é realmente iniciado, ele dispara um evento ready
, ao qual podemos vincular. Neste ponto, podemos instanciar a janela principal do nosso aplicativo:
var mainWindow = null; app.on('ready', function() { mainWindow = new BrowserWindow({ width: 1024, height: 768 }); mainWindow.loadUrl('file://' + __dirname + '/windows/main/main.html'); mainWindow.openDevTools(); });
Pontos chave:
- Criamos uma nova janela criando uma nova instância do objeto
BrowserWindow
. - Ele recebe um objeto como um único argumento, permitindo definir várias configurações, entre as quais a largura e a altura padrão da janela.
- A instância da janela tem um método
loadUrl()
, permitindo-nos carregar o conteúdo de um arquivo HTML real na janela atual. O arquivo HTML pode ser local ou remoto . - A instância da janela tem um método opcional
openDevTools()
, permitindo-nos abrir uma instância do Chrome Dev Tools na janela atual para fins de depuração.
Em seguida, devemos organizar um pouco nosso código. Eu recomendo criar uma pasta windows/
em nossa pasta src/
, e onde podemos criar uma subpasta para cada janela, assim:
my-app |- src/ |-- windows/ |--- main/ |---- main.controller.js |---- main.html |---- main.view.js
… onde main.controller.js
conterá a lógica do “lado do servidor” da nossa aplicação, e main.view.js
conterá a lógica do “lado do cliente” da nossa aplicação.
O arquivo main.html
é simplesmente uma página HTML5, então podemos simplesmente iniciá-lo assim:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Password Keychain</title> </head> <body> <h1>Password Keychain</h1> </body> </html>
Neste ponto, nosso aplicativo deve estar pronto para ser executado. Para testá-lo, podemos simplesmente digitar o seguinte em nosso terminal, na raiz da pasta src
:
$ electron .
Podemos automatizar esse processo definindo o script de
start
do arquivo package.son.
Criando um aplicativo de desktop de chaveiro de senha
Para construir um aplicativo de chaveiro de senha, precisamos: - Uma maneira de adicionar, gerar e salvar senhas. - Uma maneira conveniente de copiar e remover senhas.
Gerando e salvando senhas
Um formulário simples será suficiente para inserir novas senhas. Para demonstrar a comunicação entre várias janelas no Electron, comece adicionando uma segunda janela em nosso aplicativo, que exibirá o formulário “inserir”. Como abriremos e fecharemos esta janela várias vezes, devemos agrupar a lógica em um método para que possamos simplesmente chamá-lo quando necessário:
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; }); }
Pontos chave:
- Precisaremos definir a propriedade show como false no objeto options do construtor BrowserWindow, para evitar que a janela seja aberta por padrão quando as aplicações forem iniciadas.
- Precisaremos destruir a instância BrowserWindow sempre que a janela estiver disparando um evento fechado .
Abrindo e Fechando a Janela “Inserir”
A ideia é poder acionar a janela “inserir” quando o usuário final clicar em um botão na janela “principal”. Para fazer isso, precisaremos enviar uma mensagem da janela principal para o Processo Principal instruindo-o a abrir a janela de inserção. Podemos conseguir isso usando o módulo IPC da Electron. Na verdade, existem duas variantes do módulo IPC:
- Um para o processo principal, permitindo que o aplicativo assine mensagens enviadas do Windows.
- Um para o processo de renderização, permitindo que o aplicativo envie mensagens para o processo principal.
Embora o canal de comunicação do Electron seja em sua maioria unidirecional, é possível acessar o módulo IPC do Processo Principal em um Processo Renderizador usando o módulo remoto. Além disso, o Processo Principal pode enviar uma mensagem de volta ao Processo Renderizador do qual o evento se originou usando o método Event.sender.send().
Para usar o módulo IPC, basta exigi-lo como qualquer outro módulo NPM em nosso script Main Process:
var ipc = require('ipc');
… e, em seguida, vincule a eventos com o método on()
:
ipc.on('toggle-insert-view', function() { if(!insertWindow) { createInsertWindow(); } return (!insertWindow.isClosed() && insertWindow.isVisible()) ? insertWindow.hide() : insertWindow.show(); });
Pontos chave:
- Podemos nomear o evento como quisermos, o exemplo é apenas arbitrário.
- Não se esqueça de verificar se a instância BrowserWindow já foi criada, caso contrário, instancie-a.
- A instância BrowserWindow tem alguns métodos úteis:
- isClosed() retorna um booleano, independentemente de a janela estar ou não em um estado
closed
. - isVisible() : retorna um booleano, independentemente de a janela estar ou não visível no momento.
- show() / hide() : métodos de conveniência para mostrar e ocultar a janela.
- isClosed() retorna um booleano, independentemente de a janela estar ou não em um estado
Agora, na verdade, precisamos disparar esse evento do Renderer Process. Vamos criar um novo arquivo de script chamado main.view.js
e adicioná-lo à nossa página HTML como faríamos com qualquer script normal:
<script src="./main.view.js"></script>
Carregar o arquivo de script por meio da tag de
script
HTML carrega esse arquivo em um contexto do lado do cliente. Isso significa que, por exemplo, variáveis globais estão disponíveis viawindow.<varname>
. Para carregar um script em um contexto do lado do servidor, podemos usar o métodorequire()
diretamente em nossa página HTML:require('./main.controller.js');
.
Mesmo que o script seja carregado no contexto do lado do cliente , ainda podemos acessar o módulo IPC para o Renderer Process da mesma maneira que podemos para o Main Process e, em seguida, enviar nosso 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'); }); }; });
Há também um método sendSync() disponível, caso precisemos enviar nossos eventos de forma síncrona.
Agora, tudo o que nos resta fazer para abrir a janela “inserir” é criar um botão HTML com a diretiva Angular correspondente:
<div ng-controller="MainCtrl as vm"> <button toggle-insert-view class="mdl-button"> <i class="material-icons">add</i> </button> </div>
E adicione essa diretiva como uma dependência do controlador Angular da janela principal:
angular .module('MainWindow', ['Utils']) .controller('MainCtrl', function() { var vm = this; });
Gerando senhas
Para manter as coisas simples, podemos apenas usar o módulo uuid
do NPM para gerar IDs exclusivos que atuarão como senhas para os propósitos deste tutorial. Podemos instalá-lo como qualquer outro módulo NPM, exigi-lo em nosso script 'Utils' e criar uma fábrica simples que retornará um ID exclusivo:
var uuid = require('uuid'); angular .module('Utils', []) ... .factory('Generator', function() { return { create: function() { return uuid.v4(); } }; })
Agora, tudo o que nos resta fazer é criar um botão na visualização de inserção e anexar uma diretiva a ele que ouvirá os eventos de clique no botão e chamará o 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(); }); }; }])
Salvando senhas
Neste ponto, queremos armazenar nossas senhas. A estrutura de dados para nossas entradas de senha é bastante simples:
{ "id": String "description": String, "username": String, "password": String }
Portanto, tudo o que realmente precisamos é de algum tipo de banco de dados na memória que possa opcionalmente sincronizar com o arquivo para backup. Para isso, o Loki.js parece ser o candidato ideal. Ele faz exatamente o que precisamos para este aplicativo e oferece ainda o recurso Dynamic Views , permitindo-nos fazer coisas semelhantes ao módulo de agregação do MongoDB.
As Exibições Dinâmicas não oferecem todas as funcionalidades que o módulo de agregação do MongodDB oferece. Consulte a documentação para obter mais informações.
Vamos começar criando um formulário HTML simples:
<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 agora, vamos adicionar a lógica JavaScript para lidar com postagem e salvamento do conteúdo do formulário:

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'); }); } }); }; }])
Pontos chave:
- Primeiro precisamos inicializar o banco de dados. Esse processo envolve a criação de uma nova instância do Loki Object, fornecendo o caminho para o arquivo de banco de dados como um argumento, procurando se esse arquivo de backup existe, criando-o se necessário (incluindo a coleção 'Keychain') e, em seguida, carregando o conteúdo de este arquivo na memória.
- Podemos recuperar uma coleção específica no banco de dados com o método
getCollection()
. - Um objeto de coleção expõe vários métodos, incluindo um método
insert()
, que nos permite adicionar um novo documento à coleção. - Para persistir o conteúdo do banco de dados no arquivo, o objeto Loki expõe um método
saveDatabase()
. - Precisaremos redefinir os dados do formulário e enviar um evento IPC para informar ao Processo Principal para fechar a janela assim que o documento for salvo.
Agora temos um formulário simples que nos permite gerar e salvar novas senhas. Vamos voltar à visualização principal para listar essas entradas.
Listando senhas
Algumas coisas precisam acontecer aqui:
- Precisamos ser capazes de obter todos os documentos em nossa coleção.
- Precisamos informar a view principal sempre que uma nova senha for salva para que ela possa atualizar a view.
Podemos recuperar a lista de documentos chamando o método getCollection()
no objeto Loki. Esse método retorna um objeto com uma propriedade chamada data , que é simplesmente um array de todos os documentos dessa coleção:
this.getCollection = function() { this.collection = this.db.getCollection('keychain'); return this.collection; }; this.getDocs = function() { return (this.getCollection()) ? this.getCollection().data : null; };
Podemos então chamar o getDocs() em nosso controller Angular e recuperar todas as senhas armazenadas no banco de dados, após inicializá-lo:
angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); }); });
Um pouco de template Angular, e temos uma lista de senhas:
<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>
Um bom recurso adicionado seria atualizar a lista de senhas depois de inserir uma nova. Para isso, podemos utilizar o módulo IPC da Electron. Como mencionado anteriormente, o módulo IPC do processo principal pode ser chamado em um processo renderizador para transformá-lo em um processo ouvinte, usando o módulo remoto. Aqui está um exemplo de como implementá-lo em 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(); }); }); }); }]);
Pontos chave:
- Precisaremos usar o módulo remoto por meio de seu próprio método
require()
para exigir o módulo IPC remoto do processo principal. - Podemos então configurar nosso Renderer Process como um ouvinte de eventos por meio do método
on()
e vincular funções de retorno de chamada a esses eventos.
A visualização de inserção será então responsável por despachar este evento sempre que um novo documento for salvo:
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'); });
Copiando senhas
Geralmente não é uma boa ideia exibir senhas em texto simples. Em vez disso, vamos ocultar e fornecer um botão de conveniência que permite ao usuário final copiar a senha diretamente para uma entrada específica.
Aqui, novamente, o Electron vem em nosso socorro, fornecendo-nos um módulo de área de transferência com métodos fáceis de copiar e colar não apenas conteúdo de texto, mas também imagens e 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); }); }; }]);
Como a senha gerada será uma string simples, podemos usar o método writeText()
para copiar a senha para a área de transferência do sistema. Podemos então atualizar nosso HTML de visualização principal e adicionar o botão de cópia com a diretiva copy-password
nele, fornecendo o índice do array de senhas:
<a href="#" copy-password="{{$index}}">copy</a>
Removendo senhas
Nossos usuários finais também podem gostar de excluir senhas, caso elas se tornem obsoletas. Para fazer isso, tudo o que precisamos fazer é chamar o método remove()
na coleção de chaves. Precisamos fornecer todo o documento para o 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); };
A documentação do Loki.js afirma que também podemos remover um documento pelo seu id, mas parece que não está funcionando como esperado.
Criando um menu de área de trabalho
O Electron integra-se perfeitamente ao nosso ambiente de desktop OS para fornecer uma experiência de usuário “nativa” com aparência e comportamento para nossos aplicativos. Portanto, o Electron vem com um módulo Menu, dedicado à criação de estruturas complexas de menus de desktop para nosso aplicativo.
O módulo de menu é um tema vasto e quase merece um tutorial próprio. Eu recomendo fortemente que você leia o tutorial de integração do ambiente de desktop do Electron para descobrir todos os recursos deste módulo.
Para o escopo deste tutorial atual, veremos como criar um menu personalizado, adicionar um comando personalizado a ele e implementar o comando quit padrão.
Criando e atribuindo um menu personalizado ao nosso aplicativo
Normalmente, a lógica JavaScript para um menu Electron pertence ao arquivo de script principal do nosso aplicativo, onde nosso processo principal é definido. No entanto, podemos abstraí-lo para um arquivo separado e acessar o módulo Menu através do módulo remoto:
var remote = require('remote'), Menu = remote.require('menu');
Para definir um menu simples, precisaremos usar o método buildFromTemplate()
:
var appMenu = Menu.buildFromTemplate([ { label: 'Electron', submenu: [{ label: 'Credits', click: function() { alert('Built with Electron & Loki.js.'); } }] } ]);
O primeiro item na matriz é sempre usado como o item de menu “padrão”.
O valor da propriedade
label
não importa muito para o item de menu padrão. No modo dev, sempre exibiráElectron
. Veremos mais tarde como atribuir um nome personalizado ao item de menu padrão durante a fase de construção.
Por fim, precisamos atribuir este menu personalizado como o menu padrão para nosso aplicativo com o método setApplicationMenu()
:
Menu.setApplicationMenu(appMenu);
Atalhos de teclado de mapeamento
O Electron fornece “aceleradores”, um conjunto de strings pré-definidas que mapeiam para combinações reais de teclado, por exemplo: Command+A
ou Ctrl+Shift+Z
.
O acelerador
Command
não funciona no Windows ou Linux. Para nosso aplicativo de chaveiro de senha, devemos adicionar um item de menuFile
, oferecendo dois comandos:
- Criar senha : abra a visualização de inserção com Cmd (ou Ctrl) + N
- Sair : feche o aplicativo completamente com Cmd (ou 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!!! } ] } ...
Pontos chave:
- Podemos adicionar um separador visual adicionando um item ao array com a propriedade
type
definida comoseparator
. - O acelerador
CmdOrCtrl
é compatível com teclados Mac e PC - A propriedade
selector
é compatível apenas com OSX!
Estilizando nosso aplicativo
Você provavelmente notou ao longo dos vários exemplos de código referências a nomes de classes começando com mdl-
. Para os propósitos deste tutorial, optei por usar a estrutura de interface do usuário do Material Design Lite, mas sinta-se à vontade para usar qualquer estrutura de interface do usuário de sua escolha.
Tudo o que podemos fazer com HTML5 pode ser feito em Electron; apenas tenha em mente o tamanho crescente dos binários do aplicativo e os problemas de desempenho resultantes que podem ocorrer se você usar muitas bibliotecas de terceiros.
Empacotamento de aplicativos eletrônicos para distribuição
Você fez um aplicativo Electron, parece ótimo, você escreveu seus testes e2e com Selenium e WebDriver e está pronto para distribuí-lo para o mundo!
Mas você ainda deseja personalizá-lo, dar-lhe um nome personalizado diferente do padrão “Electron” e talvez também fornecer ícones de aplicativos personalizados para plataformas Mac e PC.
Construindo com Gulp
Hoje em dia, existe um plugin Gulp para qualquer coisa que possamos pensar. Tudo o que eu tinha que fazer era digitar gulp electron
no Google, e com certeza existe um plugin gulp-electron!
Este plugin é bastante fácil de usar, desde que a estrutura de pastas detalhada no início deste tutorial seja mantida. Se não, você pode ter que mudar um pouco as coisas.
Este plugin pode ser instalado como qualquer outro plugin Gulp:
$ npm install gulp-electron --save-dev
E então podemos definir nossa tarefa 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("")); });
Pontos chave:
- a pasta
src/
não pode ser igual à pasta onde está o Gulpfile.js, nem a mesma pasta que a pasta de distribuição. - Podemos definir as plataformas para as quais desejamos exportar através do array de
platforms
. - Devemos definir uma pasta de
cache
, onde os binários do Electron serão baixados para que possam ser empacotados com nosso aplicativo. - O conteúdo do arquivo package.json do aplicativo precisa ser passado para a tarefa gulp por meio da propriedade
packageJson
. - Existe uma propriedade de
packaging
opcional, que nos permite também criar arquivos zip dos aplicativos gerados. - Para cada plataforma, existe um conjunto diferente de “recursos de plataforma” que podem ser definidos.
Adicionando ícones de aplicativos
Uma das propriedades platformResources
é a propriedade icon
, permitindo definir um ícone personalizado para nosso aplicativo:
"icon": "keychain.ico"
O OS X requer ícones com a extensão de arquivo
.icns
. Existem várias ferramentas online que nos permitem converter arquivos.png
em.ico
e.icns
gratuitamente.
Conclusão
Neste artigo, apenas arranhamos a superfície do que o Electron pode realmente fazer. Pense em ótimos aplicativos como Atom ou Slack como uma fonte de inspiração para onde você pode ir com essa ferramenta.
Espero que você tenha achado este tutorial útil, sinta-se à vontade para deixar seus comentários e compartilhar suas experiências com o Electron!