O que electron faz?
Construa aplicações desktop multiplataforma com Electron, cobrindo arquitetura de processos principal/renderizador, comunicação IPC, gerenciamento de BrowserWindow, menus, ícones de bandeja, empacotamento e práticas recomendadas de segurança. Use quando o usuário perguntar sobre Electron, precisar criar aplicações desktop, implementar funcionalidades do Electron ou construir apps desktop multiplataforma.
Visão Geral
Este guia fornece conhecimento essencial para desenvolver aplicações desktop robustas com Electron. Você aprenderá sobre:
- Arquitetura de Processos: Entenda a diferença entre o processo principal (main) e o processo de renderização (renderer).
- Comunicação IPC: Como enviar e receber mensagens entre os processos de forma segura.
- Gerenciamento de Janelas: Criação e configuração de
BrowserWindow. - Menus e Bandeja: Menus personalizados, menus de contexto e ícones de bandeja (tray).
- Empacotamento: Ferramentas e configurações para distribuir sua aplicação.
- Segurança: Boas práticas para evitar vulnerabilidades comuns.
Pré-requisitos
- Node.js (versão 18 ou superior)
- npm ou yarn
- Conhecimentos básicos de HTML, CSS e JavaScript
Configuração do Projeto
mkdir meu-app-electron
cd meu-app-electron
npm init -y
npm install electron --save-dev
Estrutura de Diretórios Recomendada
meu-app-electron/
├── main.js
├── preload.js
├── renderer/
│ ├── index.html
│ ├── style.css
│ └── renderer.js
├── package.json
└── assets/
└── icon.png
Processo Principal (main.js)
O processo principal gerencia a vida útil da aplicação e cria janelas.
const { app, BrowserWindow, ipcMain, Menu, Tray, dialog } = require('electron')
const path = require('path')
let mainWindow
function createWindow () {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
})
mainWindow.loadFile('./renderer/index.html')
// Abrir DevTools em desenvolvimento
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools()
}
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
Comunicação IPC Segura
Use contextBridge no script preload.js para expor APIs seguras ao renderizador.
preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
sendMessage: (channel, data) => ipcRenderer.send(channel, data),
onMessage: (channel, callback) => ipcRenderer.on(channel, (event, ...args) => callback(...args)),
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args)
})
processo principal (main.js)
ipcMain.handle('dialog:openFile', async () => {
const result = await dialog.showOpenDialog({ properties: ['openFile'] })
return result
})
ipcMain.on('notification:show', (event, message) => {
new Notification('Minha App', { body: message })
})
renderizador (renderer.js)
document.getElementById('open-file').addEventListener('click', async () => {
const file = await window.electronAPI.invoke('dialog:openFile')
console.log('Arquivo selecionado:', file)
})
document.getElementById('notify').addEventListener('click', () => {
window.electronAPI.sendMessage('notification:show', 'Olá do Electron!')
})
Gerenciamento de Janelas Avançado
Janelas Filhas e Modais
const childWindow = new BrowserWindow({
parent: mainWindow,
modal: true,
width: 400,
height: 300,
title: 'Configurações'
})
childWindow.loadURL('https://example.com/settings')
Janelas Frameless e Transparentes
const framelessWindow = new BrowserWindow({
width: 300,
height: 300,
frame: false,
transparent: true,
resizable: false
})
framelessWindow.loadFile('./renderer/frameless.html')
Menus e Bandeja
Menu de Aplicação
const menuTemplate = [
{
label: 'Arquivo',
submenu: [
{ label: 'Abrir', accelerator: 'CmdOrCtrl+O', click: () => openFile() },
{ type: 'separator' },
{ label: 'Sair', accelerator: 'CmdOrCtrl+Q', role: 'quit' }
]
},
{
label: 'Editar',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' }
]
}
]
const menu = Menu.buildFromTemplate(menuTemplate)
Menu.setApplicationMenu(menu)
Menu de Contexto
mainWindow.webContents.on('context-menu', (event, params) => {
const contextMenu = Menu.buildFromTemplate([
{ label: 'Recortar', role: 'cut' },
{ label: 'Copiar', role: 'copy' },
{ label: 'Colar', role: 'paste' },
{ type: 'separator' },
{ label: 'Inspecionar', click: () => mainWindow.webContents.inspectElement(params.x, params.y) }
])
contextMenu.popup()
})
Ícone de Bandeja (Tray)
const tray = new Tray(path.join(__dirname, 'assets', 'icon.png'))
const contextMenu = Menu.buildFromTemplate([
{ label: 'Mostrar Janela', click: () => mainWindow.show() },
{ label: 'Sair', click: () => app.quit() }
])
tray.setToolTip('Minha App Electron')
tray.setContextMenu(contextMenu)
tray.on('click', () => {
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
})
Segurança
Configurações Recomendadas
const win = new BrowserWindow({
webPreferences: {
contextIsolation: true, // Isolar contexto do renderizador
nodeIntegration: false, // Desabilitar Node.js no renderizador
sandbox: true, // Executar em sandbox
webSecurity: true, // Ativar segurança web padrão
allowRunningInsecureContent: false,
experimentalFeatures: false
}
})
Content Security Policy (CSP)
Defina um CSP estrito no HTML:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
Empacotamento
Use electron-builder ou electron-packager para empacotar sua aplicação.
Instalação
npm install electron-builder --save-dev
Configuração (package.json)
{
"build": {
"appId": "com.exemplo.minha-app",
"productName": "Minha App",
"directories": {
"output": "dist"
},
"win": {
"target": "nsis",
"icon": "assets/icon.ico"
},
"mac": {
"target": "dmg",
"icon": "assets/icon.icns"
},
"linux": {
"target": "AppImage",
"icon": "assets/icon.png"
}
},
"scripts": {
"build": "electron-builder",
"dist": "electron-builder --win --mac --linux"
}
}
Notificações Nativas
const { Notification } = require('electron')
function showNotification(title, body) {
new Notification({ title, body }).show()
}
Atalhos de Teclado Globais
const { globalShortcut } = require('electron')
app.whenReady().then(() => {
globalShortcut.register('CommandOrControl+Shift+F', () => {
mainWindow.webContents.send('global-search')
})
})
app.on('will-quit', () => {
globalShortcut.unregisterAll()
})