Main Process e Renderer Process
L’architettura di Electron si basa su una separazione netta tra due tipi di processi: il Main Process e i Renderer Process. Comprendere questa distinzione è fondamentale per sviluppare applicazioni Electron sicure e performanti.
Il Main Process
Il Main Process è il processo principale dell’applicazione. Esiste un solo Main Process per applicazione ed è il primo ad essere eseguito.
Responsabilità del Main Process
- Gestire il ciclo di vita dell’applicazione (avvio, chiusura, eventi di sistema)
- Creare e gestire le finestre (
BrowserWindow) - Accedere alle API native del sistema operativo
- Gestire i menu, i dialog e il tray
- Comunicare con i Renderer Process tramite IPC
API Disponibili
Il Main Process ha accesso a:
- Tutte le API di Node.js (fs, path, child_process, etc.)
- Le API di Electron dedicate al main:
app,BrowserWindow,Menu,Tray,dialog,globalShortcut,ipcMain, etc.
// main.js - Esempio di Main Process completo
const { app, BrowserWindow, Menu, dialog } = require('electron');
const path = require('node:path');
const fs = require('node:fs');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false, // Sicurezza: disabilitato
contextIsolation: true, // Sicurezza: abilitato
},
});
mainWindow.loadFile('index.html');
mainWindow.on('closed', () => {
mainWindow = null;
});
}
app.whenReady().then(createWindow);
Il Renderer Process
Ogni finestra (BrowserWindow) esegue il proprio Renderer Process. Puoi pensare ad esso come un tab del browser — renderizza HTML, CSS e JavaScript.
Responsabilità del Renderer Process
- Renderizzare l’interfaccia utente (HTML + CSS)
- Gestire la logica dell’UI (interazioni utente, animazioni)
- Comunicare con il Main Process tramite IPC (attraverso il preload)
API Disponibili
Il Renderer Process ha accesso a:
- Tutte le Web API del browser (DOM, fetch, localStorage, Canvas, etc.)
- Le API esposte tramite il preload script via
contextBridge - Non ha accesso diretto a Node.js (per sicurezza)
// renderer.js - Esempio di Renderer Process
document.getElementById('open-btn').addEventListener('click', async () => {
// Usa l'API esposta dal preload per comunicare col Main Process
const filePath = await window.electronAPI.openFile();
if (filePath) {
document.getElementById('file-path').textContent = filePath;
}
});
Differenze Principali
| Caratteristica | Main Process | Renderer Process |
|---|---|---|
| Quanti | Uno solo | Uno per finestra |
| Accesso Node.js | Completo | No (via preload) |
| Accesso DOM | No | Completo |
| API Electron | app, BrowserWindow, Menu, etc. |
Solo via preload |
| Web API | No | Complete |
| Ruolo | Backend / Sistema | Frontend / UI |
Ciclo di Vita dell’App
Il Main Process gestisce l’intero ciclo di vita dell’applicazione attraverso gli eventi dell’oggetto app:
const { app } = require('electron');
// L'app è pronta per creare finestre
app.whenReady().then(() => {
console.log('App pronta!');
createWindow();
});
// Tutte le finestre sono state chiuse
app.on('window-all-closed', () => {
// Su macOS le app restano attive fino a Cmd+Q
if (process.platform !== 'darwin') {
app.quit();
}
});
// (macOS) L'app è stata attivata dal dock
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// L'app sta per chiudersi
app.on('before-quit', () => {
console.log('Salvataggio in corso...');
});
// Impedire la chiusura della finestra (es. per chiedere conferma)
app.on('will-quit', (event) => {
// event.preventDefault(); // Impedisce la chiusura
});
Comunicazione tra Processi
Main e Renderer Process sono isolati tra loro. Non possono accedere direttamente alle variabili o funzioni dell’altro. La comunicazione avviene tramite IPC (Inter-Process Communication):
┌──────────────┐ IPC ┌──────────────┐
│ Main Process │ ◄──────────────────► │ Renderer │
│ │ ipcMain/ipcRenderer│ Process │
│ - Node.js │ + │ - DOM │
│ - API OS │ contextBridge │ - Web API │
│ - Finestre │ │ - UI Logic │
└──────────────┘ └──────────────┘
La comunicazione IPC è trattata in dettaglio nella sezione dedicata.
Multiple Renderer Process
Ogni finestra dell’app ha il proprio Renderer Process indipendente:
function createMainWindow() {
const mainWindow = new BrowserWindow({ width: 1200, height: 800 });
mainWindow.loadFile('index.html');
// Renderer Process #1
}
function createSettingsWindow() {
const settingsWindow = new BrowserWindow({ width: 600, height: 400 });
settingsWindow.loadFile('settings.html');
// Renderer Process #2
}
I Renderer Process non possono comunicare direttamente tra loro. Devono passare attraverso il Main Process come intermediario.
Best Practice
- Non abilitare
nodeIntegrationnel Renderer Process — è un rischio per la sicurezza - Sempre abilitare
contextIsolation— isola il preload dal contenuto web - Usare il preload script per esporre solo le API necessarie al renderer
- Tenere la logica pesante nel Main Process — il renderer deve concentrarsi sull’UI
- Non bloccare il Main Process — operazioni lunghe vanno eseguite in modo asincrono o in worker thread