Electron: creación y gestión de ventanas
En Electron, la ventana (Window) es el núcleo de interacción entre la aplicación y el usuario.
Todas las interfaces visuales se ejecutan enel proceso de renderizado (Renderer Process), y la creación y control de estas ventanas la lleva a cabo elproceso principal (Main Process)que es responsable.
Creación y gestión de ventanas
Electron medianteBrowserWindowla clase para crear ventanas.
En el proceso principal, se define una nueva ventana de la siguiente manera:
Ejemplo
let mainWindow = new BrowserWindow({
width: 1200,
height: 800,
show: false, // esperar a que la página esté lista antes de mostrar
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
Opciones de configuración de BrowserWindow
width/height: ancho y alto de la ventana.minWidth/minHeight: límite de tamaño mínimo.resizable: si se permite redimensionar.fullscreen/fullscreenable: si está en pantalla completa.frame: si se muestra la barra de título nativa (normalmente se desactiva al personalizar la barra de título).transparent: ventana transparente (permite lograr efectos como cristal esmerilado, flotantes, etc.).alwaysOnTop: si la ventana está siempre al frente.
Tamaño, posición y estado de la ventana
Se puede controlar el estado de la ventana mediante API:
mainWindow.maximize() // 最大化
mainWindow.minimize() // 最小化
mainWindow.restore() // 还原
mainWindow.setBounds({ x: 100, y: 100, width: 800, height: 600 }) // 调整位置和大小
También se pueden escuchar los cambios de la ventana:
mainWindow.on('resize', () => console.log('窗口尺寸变化'))
mainWindow.on('move', () => console.log('窗口移动'))
Ventana sin bordes y barra de título personalizada
Configurarframe: falsedespués, se puede personalizar el área de la barra de título y simular el arrastre con CSS y JS:
.titlebar {
-webkit-app-region: drag;
}
Tenga en cuenta que las áreas no arrastrables (como botones) deben agregar:
.titlebar {
-webkit-app-region: drag;
}
Escucha de eventos de la ventana
mainWindow.on('close', (e) => {
e.preventDefault()
mainWindow.hide()
})
Los eventos comunes incluyen:
ready-to-show: la página ha terminado de cargar, se puede mostrar de forma segura.focus/blur: la ventana gana o pierde el foco.closed: la ventana ha sido destruida.
Cargar el contenido de la página
Las ventanas de Electron pueden cargar archivos HTML locales o sitios web remotos.
Cargar un archivo HTML local
mainWindow.loadFile('index.html')
Cargar una URL remota
mainWindow.loadURL('https://example.com')
Usar frameworks de frontend
Si usas frameworks modernos como Vue, React o Angular, puedes cargar los archivos estáticos generados en la ventana:
mainWindow.loadFile(path.join(__dirname, 'dist/index.html'))
O, en modo de desarrollo, cargar el servicio local:
mainWindow.loadURL('http://localhost:5173')
Comunicación entre ventanas
En aplicaciones de varias ventanas, puede ser necesario transferir datos o controlar el comportamiento entre ventanas.
Relación entre ventana padre e hija
Se puede especificar al crear una nueva ventanaparent:
let child = new BrowserWindow({
parent: mainWindow,
modal: true, // 模态窗口
width: 400,
height: 300,
})
Cuando la ventana padre se cierra, la ventana hija también se cierra automáticamente.
Compartir datos entre múltiples ventanas
Se puede utilizar el proceso principal como intermediario para transferir datos entre diferentes procesos de renderizado, también se pueden usar objetos globales oipcMain / ipcRenderer。
Envío de mensajes entre ventanas
El proceso principal puede operar directamente sobre la ventana objetivo:
child.webContents.send('update-data', someData)
Recibir en la ventana hija:
const { ipcRenderer } = require('electron')
ipcRenderer.on('update-data', (event, data) => {
console.log(data)
})
Optimización de la interfaz
Pantalla de inicio (Splash Screen)
Cuando la ventana principal tarda en cargar, se puede mostrar primero una ventana de inicio ligera:
const splash = new BrowserWindow({ width: 400, height: 300, frame: false })
splash.loadFile('splash.html')
mainWindow.once('ready-to-show', () => {
splash.close()
mainWindow.show()
})
Optimización de la visualización de ventanas
Para evitar el parpadeo de la pantalla en blanco, las técnicas comunes incluyen:
- Usar
show: falsepara crear la ventana, y cuando termine de cargar, luegoshow()。 - establecer el color de fondo en la página de renderizado.
- Precargar recursos (iconos, fuentes, estilos).
Técnicas para evitar parpadeos
- Activar la aceleración por GPU o desactivar la aceleración por hardware (según el caso).
- Evitar animaciones o degradados complejos en CSS.
- Usar renderizado de doble búfer para reducir el parpadeo del dibujo.
Aplicación Electron de múltiples ventanas (incluye página de inicio, ventana principal y ventana secundaria)
Estructura de directorios
my-electron-app/ ├── package.json ├── main.js # 主进程入口 ├── preload.js # 预加载脚本 ├── splash.html # 启动画面 ├── index.html # 主窗口页面 ├── child.html # 子窗口页面 └── renderer.js # 渲染进程逻辑
main.js (proceso principal)
Ejemplo
const path = require('path')
let mainWindow, splash, childWindow
function createSplash() {
splash = new BrowserWindow({
width: 400,
height: 300,
frame: false,
transparent: true,
alwaysOnTop: true,
})
splash.loadFile('splash.html')
}
function createMainWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
show: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
mainWindow.loadFile('index.html')
// mostrar después de que la página haya terminado de cargar
mainWindow.once('ready-to-show', () => {
if (splash) splash.close()
mainWindow.show()
})
mainWindow.on('closed', () => (mainWindow = null))
}
function createChildWindow() {
childWindow = new BrowserWindow({
parent: mainWindow,
modal: true,
width: 400,
height: 300,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
childWindow.loadFile('child.html')
}
// comunicación entre procesos
ipcMain.on('open-child', () => {
if (!childWindow) createChildWindow()
})
ipcMain.handle('get-app-info', () => {
return {
name: app.getName(),
version: app.getVersion(),
}
})
app.on('ready', () => {
createSplash()
setTimeout(() => createMainWindow(), 1500)
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
preload.js(script de precarga)
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
openChild: () => ipcRenderer.send('open-child'),
getAppInfo: () => ipcRenderer.invoke('get-app-info'),
})
index.html (ventana principal)
Ejemplo
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>Ventana principal</title>
<style>
body { font-family: sans-serif; text-align: center; margin-top: 100px; }
button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
</style>
</head>
<body>
<h1>Bienvenido a la aplicación Electron</h1>
<p id="info"></p>
<button id="open">Abrir ventana secundaria</button>
<script src="renderer.js"></script>
</body>
</html>
child.html (ventana secundaria)
Ejemplo
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>Ventana secundaria</title>
<style>
body { font-family: sans-serif; text-align: center; margin-top: 100px; }
</style>
</head>
<body>
<h2>Esta es una ventana secundaria</h2>
<p>Puede recibir instrucciones de la ventana principal</p>
</body>
</html>
splash.html (pantalla de inicio)
Ejemplo
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>Cargando...</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: linear-gradient(135deg, #8EC5FC, #E0C3FC);
font-family: sans-serif;
}
h1 {
font-size: 28px;
color: #333;
}
</style>
</head>
<body>
<h1>La aplicación se está iniciando, por favor espere...</h1>
</body>
</html>
renderer.js (lógica de renderizado)
Ejemplo
const btn = document.getElementById('open')
// mostrar información de la aplicación
window.electronAPI.getAppInfo().then((appInfo) => {
info.textContent =`Nombre de la aplicación: ${appInfo.name}| Versión: ${appInfo.version}`
})
// abrir la ventana secundaria
btn.addEventListener('click', () => {
window.electronAPI.openChild()
})
Después de ejecutar, el flujo es el siguiente:
- Al iniciar se muestrala pantalla de inicio(
splash.html)。 - Una vez completada la carga, la página de inicio se cierra automáticamente y se muestrala ventana principal。
- La ventana principal puede abrir mediante un botónla ventana secundaria, mostrando la funcionalidad de comunicación IPC.
- La ventana principal utilizaPreload expone la API de forma segura, evitando el acceso directo a Node.js.
- Todas las ventanas utilizanescucha de eventos、visualización retardaday otras estrategias de optimización para evitar pantallas en blanco y parpadeos.