Arquitectura de Electron
Se puede considerar Electron como:
Un contenedor de aplicaciones multiproceso, con un motor de navegador integrado (Chromium) y un runtime de Node.js,
Funciona de manera coordinada mediante comunicación entre procesos (IPC), logrando aplicaciones de escritorio de "página web + capacidades del sistema".
Sus componentes principales son:
| Rol | Descripción | Analogía |
|---|---|---|
| Main Process (proceso principal) | El cerebro de Electron; controla el ciclo de vida de la aplicación, crea ventanas y llama a las API del sistema | Administrador del sistema operativo |
| Renderer Process (proceso de renderizado) | Entorno web que se ejecuta en cada ventana (HTML, CSS, JS) | Pestaña del navegador |
| Preload Script (script de precarga) | Se ejecuta antes del renderizado; puede conectar las API del proceso principal para que las use la página web | "Capa intermedia" de seguridad |
| IPC (comunicación entre procesos) | Canal de comunicación entre el proceso principal y el proceso de renderizado | Línea telefónica |
| Objeto BrowserWindow | "Contenedor de ventana" creado por el proceso principal, donde se carga la página web | Ventana del navegador |
| Módulo App | Controla el ciclo de vida de la aplicación (inicio, salida) | Control general |
Proceso principal (Main Process)
El proceso principal es el "cerebro" de las aplicaciones Electron. Cada aplicación Electron tiene exactamente un proceso principal. Se encarga de:
- Crear y gestionar las ventanas de la aplicación(proceso de renderizado)
- Gestionar el ciclo de vida de la aplicación(inicio, salida, cambio entre primer plano y segundo plano)
- Interactuar con las API nativas del sistema operativo
- Gestionar componentes a nivel de sistema, como menús y diálogos
Ejemplo
const { app, BrowserWindow } = require('electron');
function createWindow() {
// Crear la ventana del navegador
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
// Cargar el index.html de la aplicación
mainWindow.loadFile('index.html');
}
// Se llama cuando Electron completa la inicialización
app.whenReady().then(createWindow);
Proceso de renderizado (Renderer Process)
El proceso de renderizado se encarga de mostrar la interfaz de usuario. Cada ventana de Electron es un proceso de renderizado independiente:
- Se ejecuta en el entorno del navegador Chromium
- Construye la interfaz utilizando HTML, CSS y JavaScript
- Cada ventana es un proceso independiente; no se afectan entre sí
- Se comunica con el proceso principal mediante IPC
Ejemplo
<!DOCTYPE html>
<html>
<head>
<title>Mi aplicación Electron</title>
</head>
<body>
<h1>Hello Electron!</h1>
<button id="btn">Haz clic en mí</button>
<script>
// JavaScript en el proceso de renderizado
document.getElementById('btn').addEventListener('click', () => {
alert('¡El botón ha sido clickeado!');
});
</script>
</body>
</html>
Scripts de precarga (Preload Scripts)
El script de precarga es el "puente" que conecta el proceso principal y el proceso de renderizado:
- Se ejecuta antes de que el proceso de renderizado cargue la página web
- Tiene acceso a las API de Node.js y al DOM
- Expone de forma segura las API al proceso de renderizado mediante contextBridge
Ejemplo
const { contextBridge, ipcRenderer } = require('electron');
// Expone API seguras al proceso de renderizado
contextBridge.exposeInMainWorld('electronAPI', {
showDialog: (message) => ipcRenderer.invoke('show-dialog', message)
});
Comunicación entre procesos (IPC)
Modos de comunicación IPC
IPC(Inter-Process Communication)Es el núcleo de comunicación de Electron.
Dado que el proceso principal y el proceso de renderizado son independientes, los datos deben transferirse a través de IPC.

Dirección de comunicación
| Tipo | El proceso principal escucha | El proceso de renderizado envía |
|---|---|---|
| Renderizado → Principal | ipcMain.on(channel, handler) |
ipcRenderer.send(channel, data) |
| Principal → Renderizado | event.sender.send(channel, data) |
ipcRenderer.on(channel, callback) |
Ejemplo básico de comunicación
Código del proceso principal:
Ejemplo
// Escuchar mensajes del proceso de renderizado
ipcMain.handle('show-dialog', async (event, message) => {
const result = await dialog.showMessageBox({
type: 'info',
message: message,
buttons: ['Aceptar', 'Cancelar']
});
return result;
});
Código del proceso de renderizado:
Ejemplo
document.getElementById('btn').addEventListener('click', async () => {
const result = await window.electronAPI.showDialog('¡Hola, Electron!');
console.log('El usuario hizo clic en:', result.response);
});
Ventajas y características de la arquitectura
Comparación de ventajas
| Característica | Electron | Desarrollo de escritorio tradicional |
|---|---|---|
| Tecnología de desarrollo | Tecnología web (HTML/CSS/JS) | Lenguajes nativos (C++/C#/Java) |
| Soporte multiplataforma | Desarrollar una vez, ejecutar en múltiples plataformas | Requiere desarrollo por separado para cada plataforma |
| Eficiencia de desarrollo | Alta, aprovecha el ecosistema web existente | Baja, requiere aprender tecnologías específicas de la plataforma |
| Rendimiento | Relativamente bajo, consume más recursos | Alto, rendimiento nativo |
| Tamaño del paquete de instalación | Grande (incluye Chromium) | Pequeño |
Características principales
- Consistencia multiplataforma: la aplicación tiene la misma apariencia y comportamiento en todos los sistemas operativos
- Aprovechamiento del ecosistema web: se pueden usar directamente paquetes npm y frameworks web
- Desarrollo rápido de prototipos: basado en tecnologías web conocidas, con un ciclo de desarrollo corto
- API enriquecidas: proporciona un conjunto completo de API para acceder a las funciones nativas del sistema
Ejemplo de aplicación práctica
Creemos una aplicación simple de administrador de archivos para demostrar la arquitectura de Electron:
Proceso principal (main.js):
Ejemplo
const fs = require('fs').promises;
const path = require('path');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1000,
height: 700,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true
}
});
mainWindow.loadFile('index.html');
}
// Manejar la lectura de archivos
ipcMain.handle('read-file', async (event, filePath) => {
try {
const content = await fs.readFile(filePath, 'utf-8');
return { success: true, content };
} catch (error) {
return { success: false, error: error.message };
}
});
// Manejar la selección de archivos
ipcMain.handle('select-file', async () => {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile'],
filters: [
{ name: 'Archivo de texto', extensions: ['txt', 'md', 'js', 'html', 'css'] }
]
});
return result;
});
app.whenReady().then(createWindow);
Script de precarga (preload.js):
Ejemplo
contextBridge.exposeInMainWorld('fileAPI', {
readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
selectFile: () => ipcRenderer.invoke('select-file')
});
Proceso de renderizado (index.html):
Ejemplo
<html>
<head>
<title>Administrador de archivos simple</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.container { max-width: 800px; margin: 0 auto; }
button { padding: 10px 15px; margin: 5px; cursor: pointer; }
#content { border: 1px solid #ccc; padding: 15px; margin-top: 10px;
min-height: 300px; white-space: pre-wrap; }
</style>
</head>
<body>
<div class="container">
<h1>Administrador de archivos simple</h1>
<button id="selectBtn">Seleccionar archivo</button>
<button id="clearBtn">Limpiar contenido</button>
<div>
<h3>Contenido del archivo:</h3>
<div id="content">Seleccione un archivo, por favor...</div>
</div>
</div>
<script>
const selectBtn = document.getElementById('selectBtn');
const clearBtn = document.getElementById('clearBtn');
const contentDiv = document.getElementById('content');
selectBtn.addEventListener('click', async () => {
const result = await window.fileAPI.selectFile();
if (!result.canceled && result.filePaths.length > 0) {
const filePath = result.filePaths[0];
const fileResult = await window.fileAPI.readFile(filePath);
if (fileResult.success) {
contentDiv.textContent = `Ruta del archivo: ${filePath}\n\n${fileResult.content}`;
} else {
contentDiv.textContent = `Error al leer el archivo: ${fileResult.error}`;
}
}
});
clearBtn.addEventListener('click', () => {
contentDiv.textContent = 'Seleccione un archivo, por favor...';
});
</script>
</body>
</html>
Otros módulos importantes (disponibles en el proceso principal)
| Nombre del módulo | Función |
|---|---|
app |
Controla el ciclo de vida de la aplicación (inicio/salida) |
Menu / MenuItem |
Crea la barra de menú de la aplicación |
Tray |
Icono de bandeja del sistema |
Notification |
Notificaciones del sistema |
dialog |
Diálogo de abrir archivo/guardar |
shell |
Abre archivos o URLs con el programa predeterminado del sistema |
nativeImage |
Manipular imágenes (iconos) |
clipboard |
Manipular el contenido del portapapeles |
powerMonitor |
Escuchar eventos de energía del sistema |
screen |
Obtener información de la pantalla (soporte para múltiples monitores) |
Arquitectura de seguridad y aislamiento de sandbox
Como el proceso de renderizado puede ejecutar páginas web, para prevenir que el código malicioso ataque el sistema, Electron implementa el siguiente diseño de seguridad:
| Política | Descripción |
|---|---|
| Desactivar NodeIntegration | Por defectofalse, para evitar que las páginas web llamen directamente a las API del sistema. |
| Habilitar ContextIsolation | Hacer que los scripts de la página y las API de Preload se ejecuten en contextos independientes. |
| Preload + contextBridge | Controlar explícitamente las API de seguridad expuestas al frontend. |
| Content Security Policy (CSP) | Restringir el origen de los scripts para prevenir XSS. |
| Modo sandbox | Se puede activar el sandbox para aislar completamente el proceso de renderizado. |
| Verificar URL remota | El contenido remoto no confiable debe pasar por una validación de lista blanca. |
Flujo de ejecución (desde el inicio hasta el renderizado)
1、启动应用 (electron .)
↓
2、执行 main.js(主进程启动)
↓
3、app.whenReady() 触发,创建 BrowserWindow
↓
4、BrowserWindow 启动新的渲染进程(Chromium 实例)
↓
5、preload.js 先执行(在隔离上下文中)
↓
6、index.html 加载并显示(运行前端框架)
↓
7、渲染进程通过 IPC 调用主进程逻辑
↓
8、主进程处理请求、返回结果
↓
9、用户关闭窗口 → 主进程监听 → app.quit()
Modelo de múltiples ventanas principal/secundario (multiproceso en paralelo)
- Una aplicación Electron siempre tiene soloun proceso principal;
- pero puede tenermúltiples procesos de renderizado (cada ventana independiente);
- La comunicación entre ellos debe realizarse a través del proceso principal o usar
ipcMain.handlepara realizar un puente asíncrono.
Diagrama:
主进程 (Main) ├── 窗口1 → 渲染进程A (index.html) ├── 窗口2 → 渲染进程B (settings.html) └── 窗口3 → 渲染进程C (dashboard.html)
Componentes internos principales de Electron (dependencias subyacentes)
| Nivel | Tecnología |
|---|---|
| Capa de renderizado de interfaz | Chromium(Blink + V8) |
| Capa de interfaz del sistema | Node.js(libuv + C++ bindings) |
| Capa de gestión de procesos | Núcleo de Electron (C++ + JavaScript) |
| Capa de lógica de aplicación | Tu código JS / TS (Main + Renderer) |
Otras extensionesTu código JS se ejecuta en el entorno Node encapsulado por Electron, mientras que la interfaz renderizada se ejecuta en el WebView de Chromium.