Elétron - Guia Rápido

Por que elétron?

O Electron permite que você crie aplicativos de desktop com JavaScript puro, fornecendo um tempo de execução com APIs nativas (sistema operacional) ricas.

Isso não significa que Electron é uma ligação JavaScript para bibliotecas de interface gráfica do usuário (GUI). Em vez disso, o Electron usa páginas da web como sua GUI, então você também pode vê-lo como um navegador Chromium mínimo, controlado por JavaScript. Portanto, todos os aplicativos de elétrons são tecnicamente páginas da web em execução em um navegador que pode aproveitar as APIs do seu sistema operacional.

Quem usa o elétron?

O Github desenvolveu o Electron para criar o editor de texto Atom. Ambos tiveram código aberto em 2014. Electron é usado por muitas empresas como Microsoft, Github, Slack, etc.

Electron foi usado para criar vários aplicativos. A seguir estão alguns aplicativos notáveis ​​-

  • Slack desktop
  • Aplicativo de desktop Wordpress
  • Código Visual Studio
  • Editor Caret Markdown
  • Nylas Email App
  • Cliente GitKraken git

Para começar a desenvolver usando o Electron, você precisa ter o Node e o npm (gerenciador de pacotes do nó) instalados. Se você ainda não os tem, vá para a configuração do nó para instalar o nó em seu sistema local. Confirme se o nó e o npm estão instalados executando os seguintes comandos em seu terminal.

node --version
npm --version

O comando acima irá gerar a seguinte saída -

v6.9.1
3.10.8

Sempre que criamos um projeto usando npm, precisamos fornecer um package.jsonarquivo, que contém todos os detalhes do nosso projeto. O npm facilita a configuração desse arquivo. Vamos montar nosso projeto de desenvolvimento.

  • Abra seu terminal / cmd, crie uma nova pasta chamada hello-world e abra essa pasta usando o comando cd.

  • Agora, para criar o arquivo package.json usando npm, use o seguinte comando.

npm init
  • Ele solicitará as seguintes informações -

Continue pressionando Enter e digite seu nome no campo “nome do autor”.

Crie uma nova pasta e abra-a usando o comando cd. Agora execute o seguinte comando para instalar o Electron globalmente.

$ npm install -g electron-prebuilt

Depois de executado, você pode verificar se o Electron está instalado da maneira correta executando o seguinte comando -

$ electron --version

Você deve obter a saída -

v1.4.13

Agora que configuramos o Electron, vamos prosseguir para a criação de nosso primeiro aplicativo usando-o.

Electron pega um arquivo principal definido em seu arquivo package.json e o executa. Este arquivo principal cria janelas de aplicativos que contêm páginas da web renderizadas e interação com a GUI nativa (interface gráfica do usuário) do seu sistema operacional.

Ao iniciar um aplicativo usando Electron, um main processé criado. Este processo principal é responsável por interagir com a GUI nativa do sistema operacional. Ele cria a GUI do seu aplicativo.

Apenas iniciar o processo principal não dá aos usuários de seu aplicativo nenhuma janela de aplicativo. Eles são criados pelo processo principal no arquivo principal usando o módulo BrowserWindow . Cada janela do navegador executa seu própriorenderer process. O processo de renderização pega um arquivo HTML que faz referência aos arquivos CSS usuais, arquivos JavaScript, imagens, etc. e o renderiza na janela.

O processo principal pode acessar a GUI nativa por meio de módulos disponíveis diretamente no Electron. O aplicativo de desktop pode acessar todos os módulos do Node, como o módulo do sistema de arquivos para manipulação de arquivos, solicitação para fazer chamadas HTTP, etc.

Diferença entre os processos principal e renderizador

O processo principal cria páginas da web criando as instâncias do BrowserWindow . Cada instância do BrowserWindow executa a página da web em seu próprio processo de renderização. Quando uma instância de BrowserWindow é destruída, o processo de renderização correspondente também é encerrado.

O processo principal gerencia todas as páginas da web e seus processos de renderização correspondentes. Cada processo de renderização é isolado e só se preocupa com a página da web em execução nele.

Nós criamos um package.jsonarquivo para o nosso projeto. Agora vamos criar nosso primeiro aplicativo de desktop usando Electron.

Criar um novo arquivo chamado main.js . Digite o seguinte código nele -

const {app, BrowserWindow} = require('electron') 
const url = require('url') 
const path = require('path')  

let win  

function createWindow() { 
   win = new BrowserWindow({width: 800, height: 600}) 
   win.loadURL(url.format ({ 
      pathname: path.join(__dirname, 'index.html'), 
      protocol: 'file:', 
      slashes: true 
   })) 
}  

app.on('ready', createWindow)

Crie outro arquivo, desta vez um arquivo HTML chamado index.html . Digite o seguinte código nele.

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Hello World!</title>
   </head>
   
   <body>
      <h1>Hello World!</h1>
      We are using node <script>document.write(process.versions.node)</script>,
      Chrome <script>document.write(process.versions.chrome)</script>,
      and Electron <script>document.write(process.versions.electron)</script>.
   </body>
</html>

Execute este aplicativo usando o seguinte comando -

$ electron ./main.js

Uma nova janela se abrirá. Será parecido com o seguinte -

Como este aplicativo funciona?

Criamos um arquivo principal e um arquivo HTML. O arquivo principal usa dois módulos - app e BrowserWindow . O módulo de aplicativo é usado para controlar o ciclo de vida do evento de seu aplicativo, enquanto o módulo BrowserWindow é usado para criar e controlar janelas do navegador.

Definimos uma função createWindow , onde estamos criando uma nova BrowserWindow e anexando uma URL a esta BrowserWindow. Este é o arquivo HTML que é renderizado e mostrado para nós quando executamos o aplicativo.

Usamos um processo de objeto Electron nativo em nosso arquivo html. Este objeto é estendido do objeto de processo Node.js e inclui todos ost=its funcionalidades ao adicionar muitos mais.

A interface do usuário de aplicativos Electron é construída usando HTML, CSS e JS. Portanto, podemos aproveitar todas as ferramentas disponíveis para o desenvolvimento front-end da web aqui também. Você pode usar ferramentas como Angular, Backbone, React, Bootstrap e Foundation para construir os aplicativos.

Você pode usar o Bower para gerenciar essas dependências de front-end. Instale o caramanchão usando -

$ npm install -g bower

Agora você pode obter todos os frameworks JS e CSS disponíveis, bibliotecas, plug-ins, etc. usando o bower. Por exemplo, para obter a versão estável mais recente do bootstrap, digite o seguinte comando -

$ bower install bootstrap

Isso fará o download do bootstrap em bower_components . Agora você pode fazer referência a essa biblioteca em seu HTML. Vamos criar uma página simples usando essas bibliotecas.

Vamos agora instalar o jquery usando o comando npm -

$ npm install --save jquery

Além disso, isso será necessário em nosso arquivo view.js. Já temos uma configuração main.js da seguinte forma -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

Abre o teu index.html arquivo e digite o seguinte código nele -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Hello World!</title>
      <link rel = "stylesheet" 
         href = "./bower_components/bootstrap/dist/css/bootstrap.min.css" />
   </head>
   
   <body>
      <div class = "container">
         <h1>This page is using Bootstrap and jQuery!</h1>
         <h3 id = "click-counter"></h3>
         <button class = "btn btn-success" id = "countbtn">Click here</button>
         <script src = "./view.js" ></script>
      </div>
   </body>
</html>

Crio view.js e insira a lógica do contador de cliques nele -

let $ = require('jquery')  // jQuery now loaded and assigned to $
let count = 0
$('#click-counter').text(count.toString())
$('#countbtn').on('click', () => {
   count ++ 
   $('#click-counter').text(count)
})

Execute o aplicativo usando o seguinte comando -

$ electron ./main.js

O comando acima irá gerar a saída como na imagem a seguir -

Você pode criar seu aplicativo nativo da mesma forma que cria sites. Se você não deseja que os usuários fiquem restritos a um tamanho de janela exato, pode aproveitar o design responsivo e permitir que os usuários usem seu aplicativo de maneira flexível.

O manuseio de arquivos é uma parte muito importante da construção de um aplicativo de desktop. Quase todos os aplicativos de desktop interagem com arquivos.

Vamos criar um formulário em nosso aplicativo que terá como entrada um Nome e um endereço de Email. Este formulário será salvo em um arquivo e uma lista será criada que mostrará isso como saída.

Configure seu processo principal usando o seguinte código no main.js arquivo -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

Agora abra o index.html arquivo e digite o seguinte código nele -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>File System</title>
      <link rel = "stylesheet" 
         href = "./bower_components/bootstrap/dist/css/bootstrap.min.css" />
      
      <style type = "text/css">
         #contact-list {
            height: 150px;
            overflow-y: auto;
         }
      </style>
   </head>
   
   <body>
      <div class = "container">
         <h1>Enter Names and Email addresses of your contacts</h1>
         <div class = "form-group">
            <label for = "Name">Name</label>
            <input type = "text" name = "Name" value = "" id = "Name" 
               placeholder = "Name" class = "form-control" required>
         </div>
         
         <div class = "form-group">
            <label for = "Email">Email</label>
            <input type = "email" name = "Email" value = "" id = "Email" 
               placeholder = "Email" class = "form-control" required>
         </div>
         
         <div class = "form-group">
            <button class = "btn btn-primary" id = "add-to-list">Add to list!</button>
         </div>
         
         <div id = "contact-list">
            <table class = "table-striped" id = "contact-table">
               <tr>
                  <th class = "col-xs-2">S. No.</th>
                  <th class = "col-xs-4">Name</th>
                  <th class = "col-xs-6">Email</th>
               </tr>
            </table>
         </div>
         
         <script src = "./view.js" ></script>
      </div>
   </body>
</html>

Agora precisamos lidar com o evento de adição. Faremos isso em nossoview.js Arquivo.

Vamos criar uma função loadAndDisplayContacts () que irá inicialmente carregar os contatos do arquivo. Depois de criar a função loadAndDisplayContacts () , criaremos um manipulador de clique em nossoadd to listbotão. Isso adicionará a entrada ao arquivo e à tabela.

Em seu arquivo view.js, insira o seguinte código -

let $ = require('jquery')
let fs = require('fs')
let filename = 'contacts'
let sno = 0

$('#add-to-list').on('click', () => {
   let name = $('#Name').val()
   let email = $('#Email').val()

   fs.appendFile('contacts', name + ',' + email + '\n')

   addEntry(name, email)
})

function addEntry(name, email) {
   if(name && email) {
      sno++
      let updateString = '<tr><td>'+ sno + '</td><td>'+ name +'</td><td>' 
         + email +'</td></tr>'
      $('#contact-table').append(updateString)
   }
}

function loadAndDisplayContacts() {  
   
   //Check if file exists
   if(fs.existsSync(filename)) {
      let data = fs.readFileSync(filename, 'utf8').split('\n')
      
      data.forEach((contact, index) => {
         let [ name, email ] = contact.split(',')
         addEntry(name, email)
      })
   
   } else {
      console.log("File Doesn\'t Exist. Creating new file.")
      fs.writeFile(filename, '', (err) => {
         if(err)
            console.log(err)
      })
   }
}

loadAndDisplayContacts()

Agora execute o aplicativo, usando o seguinte comando -

$ electron ./main.js

Depois de adicionar alguns contatos a ele, o aplicativo terá a aparência de -

Para mais fs module API calls, consulte o tutorial Node File System .

Agora podemos lidar com arquivos usando Electron. Veremos como chamar as caixas de diálogo salvar e abrir (nativas) para arquivos no capítulo de diálogos.

Usamos um módulo de nó, fs, no capítulo anterior. Veremos agora alguns outros módulos de nó que podemos usar com o Electron.

Módulo OS

Usando o módulo OS, podemos obter muitas informações sobre o sistema em que nosso aplicativo está sendo executado. A seguir estão alguns métodos que ajudam enquanto o aplicativo está sendo criado. Esses métodos nos ajudam a personalizar os aplicativos de acordo com o sistema operacional em que estão sendo executados.

Sr. Não Descrição da função
1

os.userInfo([options])

o os.userInfo()método retorna informações sobre o usuário efetivo no momento. Essas informações podem ser usadas para personalizar o aplicativo para o usuário, mesmo sem solicitar explicitamente as informações.

2

os.platform()

o os.platform()método retorna uma string que identifica a plataforma do sistema operacional. Isso pode ser usado para personalizar o aplicativo de acordo com o sistema operacional do usuário.

3

os.homedir()

o os.homedir()método retorna o diretório inicial do usuário atual como uma string. Geralmente, as configurações de todos os usuários residem no diretório inicial do usuário. Portanto, isso pode ser usado para o mesmo propósito em nosso aplicativo.

4

os.arch()

o os.arch()método retorna uma string que identifica a arquitetura da CPU do sistema operacional. Isso pode ser usado ao executar em arquiteturas exóticas para adaptar seu aplicativo a esse sistema.

5

os.EOL

Uma constante de string que define o marcador de fim de linha específico do sistema operacional. Deve ser usado sempre que terminar linhas em arquivos no sistema operacional host.

Usando o mesmo arquivo main.js e o seguinte arquivo HTML, podemos imprimir essas propriedades na tela -

<html>
   <head>
      <title>OS Module</title>
   </head>
   
   <body>
      <script>
         let os = require('os')
         document.write('User Info: ' + JSON.stringify(os.userInfo()) + '<br>' + 
            'Platform: ' + os.platform() + '<br>' + 
            'User home directory: ' +  os.homedir() + '<br>' + 
            'OS Architecture: ' + os.arch() + '<br>')
      </script>
   </body>
</html>

Agora execute o aplicativo usando o seguinte comando -

$ electron ./main.js

O comando acima irá gerar a seguinte saída -

User Info: {"uid":1000,"gid":1000,"username":"ayushgp","homedir":"/home/ayushgp",
   "shell":"/usr/bin/zsh"}
Platform: linux
User home directory: /home/ayushgp
OS Architecture: x64

Módulo de Rede

O módulo de rede é usado para trabalho relacionado à rede no aplicativo. Podemos criar servidores e conexões de soquete usando este módulo. Geralmente, o uso do módulo wrapper do npm é recomendado em vez do módulo net para tarefas relacionadas à rede.

As tabelas a seguir listam os métodos mais úteis do módulo -

Sr. Não Descrição da função
1

net.createServer([options][, connectionListener])

Cria um novo servidor TCP. O argumento connectionListener é definido automaticamente como um ouvinte para o evento 'conexão'.

2

net.createConnection(options[, connectionListener])

Um método de fábrica, que retorna um novo 'net.Socket' e se conecta ao endereço e porta fornecidos.

3

net.Server.listen(port[, host][, backlog][, callback])

Comece a aceitar conexões na porta e no host especificados. Se o host for omitido, o servidor aceitará conexões direcionadas a qualquer endereço IPv4.

4

net.Server.close([callback])

Finalmente fechado quando todas as conexões são encerradas e o servidor emite um evento 'close'.

5

net.Socket.connect(port[, host][, connectListener])

Abre a conexão para um determinado soquete. Se a porta e o host forem fornecidos, o soquete será aberto como um soquete TCP.

O módulo de rede também vem com alguns outros métodos. Para obter uma lista mais abrangente, consulte isto .

Agora, vamos criar um aplicativo de elétrons que usa o módulo de rede para criar conexões com o servidor. Precisamos criar um novo arquivo,server.js -

var net = require('net');
var server = net.createServer(function(connection) { 
   console.log('Client Connected');
   
   connection.on('end', function() {
      console.log('client disconnected');
   });
   
   connection.write('Hello World!\r\n');
   connection.pipe(connection);
});

server.listen(8080, function() { 
   console.log('Server running on http://localhost:8080');
});

Usando o mesmo arquivo main.js, substitua o arquivo HTML pelo seguinte -

<html>
   <head>
      <title>net Module</title>
   </head>
   
   <body>
      <script>
         var net = require('net');
         var client = net.connect({port: 8080}, function() {
            console.log('Connection established!');  
         });
         
         client.on('data', function(data) {
            document.write(data.toString());
            client.end();
         });
         
         client.on('end', function() { 
            console.log('Disconnected :(');
         });
      </script>
   </body>
</html>

Execute o servidor usando o seguinte comando -

$ node server.js

Execute o aplicativo usando o seguinte comando -

$ electron ./main.js

O comando acima irá gerar a seguinte saída -

Observe que nos conectamos ao servidor automaticamente e automaticamente nos desconectamos também.

Também temos alguns outros módulos de nó que podem ser usados ​​diretamente no front-end usando Electron. O uso desses módulos depende do cenário em que você os usa.

Electron nos fornece 2 módulos IPC (comunicação entre processos) chamados ipcMain e ipcRenderer.

o ipcMainO módulo é usado para se comunicar de maneira assíncrona do processo principal para os processos de renderização. Quando usado no processo principal, o módulo lida com mensagens assíncronas e síncronas enviadas de um processo renderer (página da web). As mensagens enviadas de um renderizador serão emitidas para este módulo.

o ipcRenderermódulo é usado para se comunicar de forma assíncrona de um processo de renderização para o processo principal. Ele fornece alguns métodos para que você possa enviar mensagens síncronas e assíncronas do processo do renderizador (página da web) para o processo principal. Você também pode receber respostas do processo principal.

Criaremos um processo principal e um processo de renderização que enviarão mensagens um ao outro usando os módulos acima.

Crie um novo arquivo chamado main_process.js com os seguintes conteúdos -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')
const {ipcMain} = require('electron')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

// Event handler for asynchronous incoming messages
ipcMain.on('asynchronous-message', (event, arg) => {
   console.log(arg)

   // Event emitter for sending asynchronous messages
   event.sender.send('asynchronous-reply', 'async pong')
})

// Event handler for synchronous incoming messages
ipcMain.on('synchronous-message', (event, arg) => {
   console.log(arg) 

   // Synchronous event emmision
   event.returnValue = 'sync pong'
})

app.on('ready', createWindow)

Agora crie um novo index.html arquivo e adicione o seguinte código nele.

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Hello World!</title>
   </head>
   
   <body>
      <script>
         const {ipcRenderer} = require('electron')

         // Synchronous message emmiter and handler
         console.log(ipcRenderer.sendSync('synchronous-message', 'sync ping')) 

         // Async message handler
         ipcRenderer.on('asynchronous-reply', (event, arg) => {
            console.log(arg)
         })

         // Async message sender
         ipcRenderer.send('asynchronous-message', 'async ping')
      </script>
   </body>
</html>

Execute o aplicativo usando o seguinte comando -

$ electron ./main_process.js

O comando acima irá gerar a seguinte saída -

// On your app console
Sync Pong
Async Pong

// On your terminal where you ran the app
Sync Ping
Async Ping

É recomendável não realizar cálculos de tarefas pesadas / bloqueadoras no processo de renderização. Sempre use o IPC para delegar essas tarefas ao processo principal. Isso ajuda a manter o ritmo de sua aplicação.

É muito importante que qualquer aplicativo seja amigável. Como resultado, você não deve criar caixas de diálogo usando chamadas alert (). Electron fornece uma interface muito boa para realizar a tarefa de criar caixas de diálogo. Vamos dar uma olhada nisso.

Elétron fornece um dialog módulo que podemos usar para exibir diálogos nativos do sistema para abrir e salvar arquivos, alertar, etc.

Vamos pular diretamente para um exemplo e criar um aplicativo para exibir arquivos de texto simples.

Crie um novo arquivo main.js e insira o seguinte código nele -

const {app, BrowserWindow} = require('electron') 
const url = require('url') 
const path = require('path') 
const {ipcMain} = require('electron')  

let win  

function createWindow() { 
   win = new BrowserWindow({width: 800, height: 600}) 
   win.loadURL(url.format ({ 
      pathname: path.join(__dirname, 'index.html'), 
      protocol: 'file:', 
      slashes: true 
   })) 
}  

ipcMain.on('openFile', (event, path) => { 
   const {dialog} = require('electron') 
   const fs = require('fs') 
   dialog.showOpenDialog(function (fileNames) { 
      
      // fileNames is an array that contains all the selected 
      if(fileNames === undefined) { 
         console.log("No file selected"); 
      
      } else { 
         readFile(fileNames[0]); 
      } 
   });
   
   function readFile(filepath) { 
      fs.readFile(filepath, 'utf-8', (err, data) => { 
         
         if(err){ 
            alert("An error ocurred reading the file :" + err.message) 
            return 
         } 
         
         // handle the file content 
         event.sender.send('fileData', data) 
      }) 
   } 
})  
app.on('ready', createWindow)

Este código irá abrir a caixa de diálogo aberta sempre que nosso processo principal receber uma mensagem 'openFile' de um processo de renderização. Esta mensagem redirecionará o conteúdo do arquivo de volta ao processo de renderização. Agora teremos que imprimir o conteúdo.

Agora, crie um novo index.html arquivo com o seguinte conteúdo -

<!DOCTYPE html> 
<html> 
   <head> 
      <meta charset = "UTF-8"> 
      <title>File read using system dialogs</title> 
   </head> 
   
   <body> 
      <script type = "text/javascript"> 
         const {ipcRenderer} = require('electron') 
         ipcRenderer.send('openFile', () => { 
            console.log("Event sent."); 
         }) 
         
         ipcRenderer.on('fileData', (event, data) => { 
            document.write(data) 
         }) 
      </script> 
   </body> 
</html>

Agora, sempre que executamos nosso aplicativo, uma caixa de diálogo nativa aberta aparecerá, conforme mostrado na imagem a seguir -

Depois de selecionar um arquivo para exibir, seu conteúdo será exibido na janela do aplicativo -

Este foi apenas um dos quatro diálogos que Electron oferece. Todos eles têm um uso semelhante. Depois de aprender a fazer isso usandoshowOpenDialog, então você pode usar qualquer uma das outras caixas de diálogo.

Os diálogos com a mesma funcionalidade são -

  • showSaveDialog ([browserWindow,] options [, callback])
  • showMessageDialog ([browserWindow,] options [, callback])
  • showErrorDialog (título, conteúdo)

Os aplicativos de desktop vêm com dois tipos de menus - o application menu(na barra superior) e um context menu(menu do botão direito). Aprenderemos como criar ambos neste capítulo.

Estaremos usando dois módulos - o Menu e os módulos MenuItem . Observe que os módulos Menu e MenuItem estão disponíveis apenas no processo principal. Para usar esses módulos no processo de renderização, você precisa do módulo remoto . Veremos isso quando criarmos um menu de contexto.

Agora, vamos criar um novo main.js arquivo para o processo principal -

const {app, BrowserWindow, Menu, MenuItem} = require('electron')
const url = require('url')
const path = require('path')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

const template = [
   {
      label: 'Edit',
      submenu: [
         {
            role: 'undo'
         },
         {
            role: 'redo'
         },
         {
            type: 'separator'
         },
         {
            role: 'cut'
         },
         {
            role: 'copy'
         },
         {
            role: 'paste'
         }
      ]
   },
   
   {
      label: 'View',
      submenu: [
         {
            role: 'reload'
         },
         {
            role: 'toggledevtools'
         },
         {
            type: 'separator'
         },
         {
            role: 'resetzoom'
         },
         {
            role: 'zoomin'
         },
         {
            role: 'zoomout'
         },
         {
            type: 'separator'
         },
         {
            role: 'togglefullscreen'
         }
      ]
   },
   
   {
      role: 'window',
      submenu: [
         {
            role: 'minimize'
         },
         {
            role: 'close'
         }
      ]
   },
   
   {
      role: 'help',
      submenu: [
         {
            label: 'Learn More'
         }
      ]
   }
]

const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
app.on('ready', createWindow)

Estamos construindo um menu a partir de um modelo aqui. Isso significa que fornecemos o menu como um JSON para a função e ela cuidará do resto. Agora temos que definir este menu como o menu do aplicativo.

Agora crie um arquivo HTML vazio chamado index.html e execute este aplicativo usando -

$ electron ./main.js

Na posição normal dos menus do aplicativo, você verá um menu baseado no modelo acima.

Criamos este menu a partir do processo principal. Vamos agora criar um menu de contexto para nosso aplicativo. Faremos isso em nosso arquivo HTML -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Menus</title>
   </head>
   
   <body>
      <script type = "text/javascript">
         const {remote} = require('electron')
         const {Menu, MenuItem} = remote

         const menu = new Menu()

         // Build menu one item at a time, unlike
         menu.append(new MenuItem ({
            label: 'MenuItem1',
            click() { 
               console.log('item 1 clicked')
            }
         }))
         
         menu.append(new MenuItem({type: 'separator'}))
         menu.append(new MenuItem({label: 'MenuItem2', type: 'checkbox', checked: true}))
         menu.append(new MenuItem ({
            label: 'MenuItem3',
            click() {
               console.log('item 3 clicked')
            }
         }))

         // Prevent default action of right click in chromium. Replace with our menu.
         window.addEventListener('contextmenu', (e) => {
            e.preventDefault()
            menu.popup(remote.getCurrentWindow())
         }, false)
      </script>
   </body>
</html>

Importamos os módulos Menu e MenuItem usando o módulo remoto; então, criamos um menu e anexamos nossos itens de menu a ele um por um. Além disso, evitamos a ação padrão de clicar com o botão direito no cromo e o substituímos pelo nosso menu.

A criação de menus no Electron é uma tarefa muito simples. Agora você pode anexar seus manipuladores de eventos a esses itens e manipular os eventos de acordo com suas necessidades.

A bandeja do sistema é um menu fora da janela do aplicativo. No MacOS e no Ubuntu, ele está localizado no canto superior direito da tela. No Windows, está no canto inferior direito. Podemos criar menus para nossa aplicação nas bandejas do sistema usando Electron.

Crie um novo main.jsarquivo e adicione o seguinte código a ele. Tenha um arquivo png pronto para usar no ícone da bandeja do sistema.

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

Depois de configurar uma janela básica do navegador, criaremos um novo index.html arquivo com o seguinte conteúdo -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Menus</title>
   </head>
   <body>
      <script type = "text/javascript">
         const {remote} = require('electron')
         const {Tray, Menu} = remote
         const path = require('path')

         let trayIcon = new Tray(path.join('','/home/ayushgp/Desktop/images.png'))

         const trayMenuTemplate = [
            {
               label: 'Empty Application',
               enabled: false
            },
            
            {
               label: 'Settings',
               click: function () {
                  console.log("Clicked on settings")
               }
            },
            
            {
               label: 'Help',
               click: function () {
                  console.log("Clicked on Help")
               }
            }
         ]
         
         let trayMenu = Menu.buildFromTemplate(trayMenuTemplate)
         trayIcon.setContextMenu(trayMenu)
      </script>
   </body>
</html>

Criamos a bandeja usando o submódulo Bandeja. Em seguida, criamos um menu usando um modelo e anexamos o menu ao nosso objeto de bandeja.

Execute o aplicativo usando o seguinte comando -

$ electron ./main.js

Ao executar o comando acima, verifique na bandeja do sistema o ícone usado. Usei um rosto sorridente para a minha candidatura. O comando acima irá gerar a seguinte saída -

Electron fornece notificações nativas API apenas para MacOS. Portanto, não vamos usar isso; em vez disso, usaremos um módulo npm chamado notificador de nó . Ele nos permite notificar usuários em Windows, MacOS e Linux.

Instale o módulo node-notifier na pasta do seu aplicativo usando o seguinte comando nessa pasta -

$ npm install --save node-notifier

Vamos agora criar um aplicativo que possui um botão que irá gerar uma notificação cada vez que clicarmos neste botão.

Crie um novo main.js arquivo e digite o seguinte código nele -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

Vamos agora criar nossa página da web e script que irá acionar a notificação. Crie um novoindex.html arquivo com o seguinte código -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Menus</title>
   </head>
   
   <body>
      <button type = "button" id = "notify" name = "button">
         Click here to trigger a notification!</button>
      <script type = "text/javascript">
         const notifier = require('node-notifier')
         const path = require('path');
         
         document.getElementById('notify').onclick = (event) => {
            notifier.notify ({
               title: 'My awesome title',
               message: 'Hello from electron, Mr. User!',
               icon: path.join('','/home/ayushgp/Desktop/images.png'),  // Absolute path 
                  (doesn't work on balloons)
               sound: true,  // Only Notification Center or Windows Toasters
               wait: true    // Wait with callback, until user action is taken 
               against notification
            
            }, function (err, response) {
               // Response is response from notification
            });

            notifier.on('click', function (notifierObject, options) {
               console.log("You clicked on the notification")
            });

            notifier.on('timeout', function (notifierObject, options) {
               console.log("Notification timed out!")
            });
         }
      </script>
   </body>
</html>

o notify método nos permite passar um objectwithinformações como título, mensagem, miniatura, etc. que nos ajudam a personalizar a notificação. Também podemos definir alguns ouvintes de eventos na notificação.

Agora, execute o aplicativo usando o seguinte comando -

$ electron ./main.js

Ao clicar no botão que criamos, você verá uma notificação nativa do seu sistema operacional, conforme mostrado na imagem a seguir -

Também tratamos dos eventos em que o usuário clica na notificação ou a notificação atinge o tempo limite. Esses métodos nos ajudam a tornar o aplicativo mais interativo se estiver sendo executado em segundo plano.

A tag webview é usada para incorporar o conteúdo 'convidado' como páginas da web em seu aplicativo Electron. Este conteúdo está contido no contêiner webview. Uma página incorporada em seu aplicativo controla como esse conteúdo será exibido.

O webview é executado em um processo separado do seu aplicativo. Para garantir a segurança de conteúdo malicioso, o webview não tem as mesmas permissões que sua página web. Isso mantém seu aplicativo protegido do conteúdo incorporado. Todas as interações entre seu aplicativo e a página incorporada serão assíncronas.

Vamos considerar um exemplo para entender a incorporação de uma página da web externa em nosso aplicativo Electron. Vamos incorporar o site tutorialspoint em nosso aplicativo no lado direito. Crie um novomain.js arquivo com o seguinte conteúdo -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

Agora que configuramos nosso processo principal, vamos criar o arquivo HTML que irá incorporar o site tutorialspoint. Crie um arquivo chamado index.html com o seguinte conteúdo -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Menus</title>
   </head>
   
   <body>
      <div>
         <div>
            <h2>We have the website embedded below!</h2>
         </div>
         <webview id = "foo" src = "https://www.tutorialspoint.com/" style = 
            "width:400px; height:480px;">
            <div class = "indicator"></div>
         </webview>
      </div>
      
      <script type = "text/javascript">
         // Event handlers for loading events.
         // Use these to handle loading screens, transitions, etc
         onload = () => {
            const webview = document.getElementById('foo')
            const indicator = document.querySelector('.indicator')

            const loadstart = () => {
               indicator.innerText = 'loading...'
            }

            const loadstop = () => {
               indicator.innerText = ''
            }

            webview.addEventListener('did-start-loading', loadstart)
            webview.addEventListener('did-stop-loading', loadstop)
         }
      </script>
   </body>
</html>

Execute o aplicativo usando o seguinte comando -

$ electron ./main.js

O comando acima irá gerar a seguinte saída -

A tag webview também pode ser usada para outros recursos. O elemento webview tem uma lista de eventos que ele emite listados nos documentos oficiais. Você pode usar esses eventos para melhorar a funcionalidade, dependendo das coisas que acontecem na visualização da web.

Sempre que você estiver incorporando scripts ou outros recursos da Internet, é aconselhável usar o webview. Isso é recomendado porque tem grandes benefícios de segurança e não impede o comportamento normal.

A captura de áudio e vídeo são características importantes se você estiver criando aplicativos para compartilhamento de tela, memos de voz, etc. Eles também são úteis se você precisar de um aplicativo para capturar a imagem do perfil.

Estaremos usando a API getUserMedia HTML5 para capturar fluxos de áudio e vídeo com Electron. Vamos primeiro configurar nosso processo principal nomain.js arquivo da seguinte forma -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')

let win

// Set the path where recordings will be saved
app.setPath("userData", __dirname + "/saved_recordings")

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

Agora que configuramos nosso processo principal, vamos criar o arquivo HTML que irá capturar este conteúdo. Crie um arquivo chamadoindex.html com o seguinte conteúdo -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Audio and Video</title>
   </head>
   
   <body>
      <video autoplay></video>
      <script type = "text/javascript">
         function errorCallback(e) {
            console.log('Error', e)
         }

         navigator.getUserMedia({video: true, audio: true}, (localMediaStream) => {
            var video = document.querySelector('video')
            video.src = window.URL.createObjectURL(localMediaStream)
            video.onloadedmetadata = (e) => {
               // Ready to go. Do some stuff.
            };
         }, errorCallback)
      </script>
   </body>
</html>

O programa acima irá gerar a seguinte saída -

Agora você tem o stream de sua webcam e de seu microfone. Você pode enviar este stream pela rede ou salvá-lo em um formato de sua preferência.

Dê uma olhada na documentação do MDN para capturar imagens para obter as imagens de sua webcam e armazená-las. Isso foi feito usando a API HTML5 getUserMedia . Você também pode capturar a área de trabalho do usuário usando o módulo desktopCapturer que vem com o Electron. Vamos agora ver um exemplo de como obter o fluxo de tela.

Use o mesmo arquivo main.js acima e edite o arquivo index.html para ter o seguinte conteúdo -

desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
   if (error) throw error
   for (let i = 0; i < sources.length; ++i) {
      if (sources[i].name === 'Your Window Name here!') {
         navigator.webkitGetUserMedia({
            audio: false,
            video: {
               mandatory: {
                  chromeMediaSource: 'desktop',
                  chromeMediaSourceId: sources[i].id,
                  minWidth: 1280,
                  maxWidth: 1280,
                  minHeight: 720,
                  maxHeight: 720
               }
            }
         }, handleStream, handleError)
         return
      }
   }
})

function handleStream (stream) {
   document.querySelector('video').src = URL.createObjectURL(stream)
}

function handleError (e) {
   console.log(e)
}

Usamos o módulo desktopCapturer para obter as informações sobre cada janela aberta. Agora você pode capturar os eventos de um aplicativo específico ou de toda a tela, dependendo do nome que você passar para o acimaif statement. Isso irá transmitir apenas o que está acontecendo naquela tela para o seu aplicativo.

Você pode consultar esta pergunta StackOverflow para entender o uso em detalhes.

Normalmente, memorizamos certos atalhos para todos os aplicativos que usamos diariamente em nosso PC. Para tornar seus aplicativos intuitivos e facilmente acessíveis ao usuário, você deve permitir que o usuário use atalhos.

Usaremos o módulo globalShortcut para definir atalhos em nosso aplicativo. Observe queAcceleratorssão Strings que podem conter vários modificadores e códigos de tecla, combinados pelo caractere +. Esses aceleradores são usados ​​para definir atalhos de teclado em todo o nosso aplicativo.

Vamos considerar um exemplo e criar um atalho. Para isso, seguiremos o exemplo das caixas de diálogo onde utilizamos a caixa de diálogo abrir para abrir arquivos. Vamos registrar umCommandOrControl+O atalho para abrir a caixa de diálogo.

Nosso main.jso código permanecerá o mesmo de antes. Então, crie um novomain.js arquivo e digite o seguinte código nele -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')
const {ipcMain} = require('electron')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

ipcMain.on('openFile', (event, path) => {
   const {dialog} = require('electron')
   const fs = require('fs')
   dialog.showOpenDialog(function (fileNames) {
         
      // fileNames is an array that contains all the selected
      if(fileNames === undefined)
         console.log("No file selected")
      else
         readFile(fileNames[0])
   })

   function readFile(filepath){
      fs.readFile(filepath, 'utf-8', (err, data) => {
         if(err){
            alert("An error ocurred reading the file :" + err.message)
            return
         }
         
         // handle the file content
         event.sender.send('fileData', data)
      })
   }
})

app.on('ready', createWindow)

Este código irá abrir a caixa de diálogo aberta sempre que nosso processo principal receber uma mensagem 'openFile' de um processo renderizador. Anteriormente, essa caixa de diálogo aparecia sempre que o aplicativo era executado. Vamos agora limitar a abertura apenas quando pressionamosCommandOrControl+O.

Agora crie um novo index.html arquivo com o seguinte conteúdo -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>File read using system dialogs</title>
   </head>
   
   <body>
      <p>Press CTRL/CMD + O to open a file. </p>
      <script type = "text/javascript">
         const {ipcRenderer, remote} = require('electron')
         const {globalShortcut} = remote
         globalShortcut.register('CommandOrControl+O', () => {
            ipcRenderer.send('openFile', () => {
               console.log("Event sent.");
            })
            
            ipcRenderer.on('fileData', (event, data) => {
               document.write(data)
            })
         })
      </script>
   </body>
</html>

Registramos um novo atalho e passamos um callback que será executado sempre que pressionarmos este atalho. Podemos cancelar o registro de atalhos quando não os exigirmos.

Agora, uma vez que o aplicativo seja aberto, obteremos a mensagem para abrir o arquivo usando o atalho que acabamos de definir.

Esses atalhos podem ser personalizados, permitindo que o usuário escolha seus próprios atalhos para ações definidas.

As variáveis ​​de ambiente controlam a configuração e o comportamento do aplicativo sem alterar o código. Certos comportamentos do Electron são controlados por variáveis ​​de ambiente porque são inicializados antes dos sinalizadores de linha de comando e do código do aplicativo.

Existem dois tipos de variáveis ​​de ambiente codificadas em elétrons - Production variables e Development variables.

Variáveis ​​de Produção

As seguintes variáveis ​​de ambiente são destinadas ao uso em tempo de execução em aplicativos Electron empacotados.

Sr. Não Variável e Descrição
1

GOOGLE_API_KEY

Electron inclui uma chave de API codificada para fazer solicitações ao serviço da web de geocodificação do Google. Como essa chave API está incluída em todas as versões do Electron, ela geralmente excede sua cota de uso.

Para contornar isso, você pode fornecer sua própria chave de API do Google no ambiente. Coloque o seguinte código em seu arquivo de processo principal, antes de abrir qualquer janela do navegador que fará solicitações de geocodificação -

process.env.GOOGLE_API_KEY = 'YOUR_KEY_HERE'
2

ELECTRON_RUN_AS_NODE

Inicia o processo como um processo normal do Node.js.

3

ELECTRON_FORCE_WINDOW_MENU_BAR (Linux Only)

Não use a barra de menu global no Linux.

Variáveis ​​de Desenvolvimento

As seguintes variáveis ​​de ambiente são destinadas principalmente para fins de desenvolvimento e depuração.

Sr. Não Variável e Descrição
1

ELECTRON_ENABLE_LOGGING

Imprime o registro interno do Chrome no console.

2

ELECTRON_ENABLE_STACK_DUMPING

Imprime o rastreamento de pilha no console quando o Electron falha.

3

ELECTRON_DEFAULT_ERROR_MODE

Mostra a caixa de diálogo de travamento do Windows quando o Electron trava.

Para definir qualquer uma dessas variáveis ​​de ambiente como verdadeira, defina-a em seu console. Por exemplo, se você deseja habilitar o registro, use os seguintes comandos -

Para Windows

> set ELECTRON_ENABLE_LOGGING=true

Para Linux

$ export ELECTRON_ENABLE_LOGGING=true

Observe que você precisará definir essas variáveis ​​de ambiente sempre que reiniciar o computador. Se você quiser evitar isso, adicione estas linhas ao seu.bashrc arquivos.

Temos dois processos que executam nosso aplicativo - o processo principal e o processo de renderização.

Como o processo de renderização é o que está sendo executado na janela do navegador, podemos usar o Chrome Devtools para depurá-lo. Para abrir DevTools, use o atalho "Ctrl + Shift + I" ou a tecla <F12>. Você pode verificar como usar devtools aqui .

Ao abrir o DevTools, seu aplicativo terá a aparência mostrada na imagem a seguir -

Depurando o Processo Principal

O DevTools em uma janela do navegador Electron só pode depurar o JavaScript executado nessa janela (ou seja, as páginas da web). Para depurar o JavaScript que é executado no processo principal, você precisará usar um depurador externo e iniciar o Electron com a opção --debug ou --debug-brk .

O Electron escutará as mensagens do protocolo do depurador V8 na porta especificada; um depurador externo precisará se conectar a esta porta. A porta padrão é 5858.

Execute seu aplicativo usando o seguinte -

$ electron --debug = 5858 ./main.js

Agora você precisará de um depurador que suporte o protocolo do depurador V8. Você pode usar o VSCode ou o node-inspector para essa finalidade. Por exemplo, vamos seguir essas etapas e configurar o VSCode para esse propósito. Siga estas etapas para configurá-lo -

Baixe e instale o VSCode . Abra seu projeto Electron no VSCode.

Adicionar um arquivo .vscode/launch.json com a seguinte configuração -

{
   "version": "1.0.0",
   "configurations": [
      {
         "name": "Debug Main Process",
         "type": "node",
         "request": "launch",
         "cwd": "${workspaceRoot}",
         "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
         "program": "${workspaceRoot}/main.js"
      }
   ]
}

Note - Para Windows, use "${workspaceRoot}/node_modules/.bin/electron.cmd" para runtimeExecutable.

Defina alguns pontos de interrupção em main.jse inicie a depuração na Visualização de depuração. Quando você atinge os pontos de interrupção, a tela se parece com isto -

O depurador VSCode é muito poderoso e o ajudará a corrigir erros rapidamente. Você também tem outras opções comonode-inspector para depurar aplicativos de elétrons.

Empacotar e distribuir aplicativos é parte integrante do processo de desenvolvimento de um aplicativo de desktop. Como o Electron é uma estrutura de desenvolvimento de aplicativos de desktop multiplataforma, o empacotamento e a distribuição de aplicativos para todas as plataformas também devem ser uma experiência perfeita.

A comunidade de elétrons criou um projeto, empacotador de elétrons, que cuida do mesmo para nós. Ele nos permite empacotar e distribuir nosso aplicativo Electron com pacotes específicos do sistema operacional (.app, .exe etc) via JS ou CLI.

Plataformas Suportadas

Electron Packager é executado nas seguintes plataformas host -

  • Windows (32/64 bits)
  • OS X
  • Linux (x86 / x86_64)

Ele gera executáveis ​​/ pacotes para as seguintes plataformas de destino -

  • Windows (também conhecido como win32, para 32/64 bits)
  • OS X (também conhecido como darwin) / Mac App Store (também conhecido como mas)
  • Linux (para arquiteturas x86, x86_64 e armv7l)

Instalação

Instale o empacotador de elétrons usando -

# for use in npm scripts
$ npm install electron-packager --save-dev

# for use from cli
$ npm install electron-packager -g

Aplicativos de embalagem

Nesta seção, veremos como executar o empacotador a partir da linha de comando. A forma básica do comando é -

electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...]

Isso vai -

  • Encontre ou baixe a versão correta do Electron.

  • Use essa versão do Electron para criar um aplicativo em <output-folder> / <appname> - <platform> - <arch>.

--platform e --archpode ser omitido, em dois casos. Se você especificar--allem vez disso, serão criados pacotes para todas as combinações válidas de plataformas / arquiteturas de destino. Caso contrário, um único pacote para a plataforma / arquitetura do host será criado.

Usamos os seguintes recursos para aprender mais sobre o Electron. Nós nos referimos a eles ao criar este tutorial.

O recurso mais importante é a documentação do Electron. A documentação cobre extensivamente quase todos os recursos e peculiaridades da estrutura. Eles estão sozinhos o suficiente para criar um aplicativo.

Existem também alguns exemplos de elétrons muito bons apresentados no repositório de aplicativos de amostra de elétrons.

Recursos de Vídeo

Aplicativos de desktop com idiomas da web

Desenvolvimento rápido de aplicativos de plataforma cruzada para desktop usando JavaScript e Electron

Postagens no blog

Construindo um aplicativo de desktop com Electron

Construir um reprodutor de música com React e Electron

Criando seu primeiro aplicativo de desktop com HTML, JS e Electron

Crie aplicativos de nó de desktop multiplataforma com Electron