Saltar al contenido principal

Construye tu primera aplicación

Objetivos de aprendizaje

En esta parte del tutorial, aprenderás a configurar tu proyecto Electron y escribir una aplicación inicial sencilla. Al final de esta sección, podrás ejecutar una aplicación de trabajo Electron en modo de desarrollo desde tu terminal.

Configurando tu Proyecto

Evita WSL

Si estás en una máquina con Windows, por favor no utilices el Subsistema de Windows para Linux (WSL) mientras sigues este tutorial, porque puedes experimentar errores al tratar de ejecutar la aplicación.

Inicializando su proyecto con npm

Las aplicaciones Electron se organizan usando npm, con el archivo package.json como punto de entrada. Comienza creando una carpeta e inicialiando un paquete npm dentro de ella con el comando npm init.

mkdir my-electron-app && cd my-electron-app
npm init

Este comando le pedirá que configure algunos campos en su package.json. Hay algunas reglas que seguir para este tutorial:

  • El punto de entrada debe ser el archivo main.js (crearás ese archivo pronto).
  • autor, licencia, y descripción pueden ser cualquier valor, pero serán necesarios para el empaquetado más adelante.

Luego, instale Electron en las devDependencies de su aplicación, ésta es la lista de dependencias de paquetes externos exclusiva al entorno de desarrollo y que no se requieren en producción.

Why is Electron a devDependency?

This may seem counter-intuitive since your production code is running Electron APIs. Sin embargo, las aplicaciones empaquetadas vendrán con el binario de Electron, eliminando la necesidad de especificarlo como una dependencia de la producción.

npm install electron --save-dev

Tu archivo package.json debería verse algo así luego de inicializar tu paquete e instalar Electron. Ahora también deberías tener una carpeta node_modules que contenga el ejecutable de Electron, así como un archivo de bloqueo package-lock.json que especifica las versiones de dependencia exactas para instalar.

package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "23.1.3"
}
}
Instrucciones de Instalación Avanzada de Electron

Si falla la instalación de Electron directamente, consulte nuestra documentación de Instalación avanzada para obtener instrucciones sobre descargas de mirrors, proxies y pasos para la resolución de problemas.

Agregando un .gitignore

The .gitignore file specifies which files and directories to avoid tracking with Git. Deberás agregar una copia de Plantilla gitignore de GitHub para Node.js en la carpeta raíz de tu proyecto para evitar que tu carpeta node_modules se agregue a tu repositorio de GitHub mediante un commit.

Ejecutar una aplicación Electron

:::Leer más

Lea la documentación del modelo de proceso de Electron para comprender mejor cómo funcionan juntos los múltiples procesos de Electron.

:::

El script main que definiste en package.json es el punto de entrada de cualquier aplicación de Electron. Este script controla el proceso principal, que se ejecuta en el entorno de Node.js. Y es responsable de controlar el ciclo de vida de su aplicación, mostrando interfaces nativas, realizando operaciones privilegiadas y gestionando procesos de renderizado (más sobre eso más adelante).

Antes de crear su primera aplicación Electron, utilizará un script trivial para garantizar que el punto de entrada del proceso principal está configurado correctamente. Cree un archivo main.js en la carpeta raíz de su proyecto con una sola línea de código:

main.js
console.log('Hello from Electron 👋')

Debido a que el proceso principal de Electron es un runtime de Node.js, puede ejecutar código Node.js arbitrariamente con el comando electron (incluso puedes usarlo como REPL). Para ejecutar este script, agregue electron . al comando start en el campo de scripts de su archivo package.json. Este comando le indicará al ejecutable de Electron que busque el script main en el directorio actual y lo ejecute en modo de desarrollo.

package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"scripts": {
"start": "electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "23.1.3"
}
}
npm run start

Su terminal debería imprimir Hello from Electron 👋. Felicitaciones, has ejecutado tu primera línea de código en Electron! A continuación aprenderás cómo crear interfaces de usuario con HTML y cargarlas en una ventana nativa.

Cargando una página web en un BrowserWindow de Electron

En Electron, cada ventana muestra una página web que puede cargarse desde un archivo HTML local o desde una URL remota. Para este ejemplo, la cargaremos en un archivo local. Comienza creando una página web básica en un archivo index.html en la carpeta raíz de tu proyecto:

index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
</body>
</html>

Ahora que tienes una página web, puedes cargarla en un BrowserWindow de Electron. Reemplaza el contenido de tu archivo main.js por el siguiente código. Explicaremos cada bloque resaltado por separado.

main.js
const { app, BrowserWindow } = require('electron')

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})

win.loadFile('index.html')
}

app.whenReady().then(() => {
createWindow()
})

Importando módulos

main.js (Line 1)
const { app, BrowserWindow } = require('electron')

En la primera línea, estamos importando dos módulos Electron con la sintaxis del módulo CommonJS:

  • app, que controla el ciclo de vida del evento de su aplicación.
  • BrowserWindow, que crea y gestiona ventanas de aplicaciones.
Convenciones de capitalización de módulos

Puede que hayas notado la diferencia de mayúsculas entre los módulos unpp y BrowserWindow. Electron sigue las convenciones típicas de JavaScript aquí, donde los módulos PascalCase son constructores de clases instanciables (p. ej. BrowserWindow, Tray, Notification) mientras que los módulos camelCase no son instanciables (por ejemplo, app, ipcRenderer, webContents).

Alias de importación tipado

Para un mejor chequeo de tipos al utilizar código TypeScript, puedes optar por importar los módulos de procesos de main desde electron/main.

const { app, BrowserWindow } = require('electron/main')

Para obtener más información, consulta la documentación de Modelo de Proceso.

Módulos ES en Electron

los módulos ECMAScript (es decir, usar import para cargar un módulo) son soportados en Electron a partir de Electron 28. Puedes encontrar más información sobre el estado de ESM en Electron y cómo usarlos en nuestra aplicación en nuestra guía de ESM.

Escribiendo una función reutilizable para instanciar ventanas

La función createWindow() carga tu página web en una nueva instancia BrowserWindow:

main.js (Lines 3-10)
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})

win.loadFile('index.html')
}

Llamar a tu función cuando la app esté lista

main.js (Lines 12-14)
app.whenReady().then(() => {
createWindow()
})

Muchos de los módulos principales de Electron son emisores de eventos de Node.js que se adhieren a la arquitectura asincrónica de Node. El módulo app es uno de estos emisores.

En Electron, BrowserWindows solo se puede crear después de que el evento del módulo app ready sea disparado. Puedes esperar a este evento usando la API app.whenReady() y llamando a createWindow() una vez que se cumpla su promesa.

info

Usted normalmente escucha los eventos de Node.js usando la función .on de un emisor.

+ app.on('ready', () => {
- app.whenReady().then(() => {
createWindow()
})

Sin embargo, Electron expone a app.whenReady() como un ayudante específico para el evento ready con el fin de evitar dificultades al escuchar directamente ese evento en particular. Vea electron/electron#21972 para más detalles.

Nota: ¡En este punto, utilizar el comando start de su aplicación de Electron debería abrir correctamente una ventana que muestre su página web!

Cada página web que tu aplicación muestre en una ventana se ejecutará en un proceso separado llamado proceso renderizador (o simplemente renderizador para abreviar). Los procesos de renderizado tienen acceso a las mismas API de JavaScript y herramientas que usted utiliza para el desarrollo típico de la web front-end tales como usar webpack para empaquetar y minimizar tu código o React para construir tus interfaces de usuario.

Gestionar el ciclo de vida de ventanas de tu aplicación

Las ventanas de la aplicación se comportan de forma diferente en cada sistema operativo. En vez de hacer cumplir estas convenciones de forma predeterminada, Electron le ofrece la opción de implementarlas en el código de su aplicación si así lo desea. Puedes implementar convenciones de la ventana básica escuchando los eventos emitidos por la aplicación y los módulos BrowserWindow.

Flujo de control específico al Proceso

Comprobar la variable process.platform de Node puede ayudarte a ejecutar el código condicionalmente en ciertas plataformas. Tenga en cuenta que solo hay tres plataformas posibles que Electron puede ejecutar en: win32 (Windows), linux (Linux), y darwin (macOS).

Salir de la aplicación cuando todas las ventanas estén cerradas (Windows & Linux)

En Windows y Linux, al salir de todas las ventanas generalmente se cierra una aplicación por completo. Para implementar este patrón en su aplicación Electron escuche el módulo de la aplicación window-all-closed evento, y llame a app.quit() para salir de la aplicación si el usuario no está en macOS.

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})

Abra una ventana si no hay ninguna abierta (macOS)

En contraste, las aplicaciones macOS generalmente siguen ejecutándose incluso sin ventanas abiertas. Activar la aplicación cuando no hay ventanas disponibles debería abrir una nueva.

Para implementar esta función, escucha el evento activate del módulo app y llame a su método existente createWindow() si no hay ventanas BrowserWindows abiertas.

Debido a que las ventanas no se pueden crear antes del evento ready, solo debería escuchar el evento activate después de inicializar tu aplicación. Haz esto solo escuchando para activar eventos dentro de tu devolución de llamada whenReady() existente.

app.whenReady().then(() => {
createWindow()

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

Final starter code

const { app, BrowserWindow } = require('electron/main')

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})

win.loadFile('index.html')
}

app.whenReady().then(() => {
createWindow()

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

Opcional: Debugging desde VS Code

Si deseas depurar tu aplicación usando VS Code, debes adjuntar VS code para ambos procesos tanto el principal y los renderizadores. Aquí hay un ejemplo de configuración para ejecutar. Crear una configuración de launch.json en una nueva carpeta .vscode en su proyecto:

.vscode/launch.json
{
"version": "0.2.0",
"compounds": [
{
"name": "Main + renderer",
"configurations": ["Main", "Renderer"],
"stopAll": true
}
],
"configurations": [
{
"name": "Renderer",
"port": 9222,
"request": "attach",
"type": "chrome",
"webRoot": "${workspaceFolder}"
},
{
"name": "Main",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"args": [".", "--remote-debugging-port=9222"],
"outputCapture": "std",
"console": "integratedTerminal"
}
]
}

The "Main + renderer" option will appear when you select "Run and Debug" from the sidebar, allowing you to set breakpoints and inspect all the variables among other things in both the main and renderer processes.

What we have done in the launch.json file is to create 3 configurations:

  • Main is used to start the main process and also expose port 9222 for remote debugging (--remote-debugging-port=9222). This is the port that we will use to attach the debugger for the Renderer. Because the main process is a Node.js process, the type is set to node.
  • Renderer is used to debug the renderer process. Because the main process is the one that creates the process, we have to "attach" to it ("request": "attach") instead of creating a new one. The renderer process is a web one, so the debugger we have to use is chrome.
  • Main + renderer is a compound task that executes the previous ones simultaneously.
advertencia

Because we are attaching to a process in Renderer, it is possible that the first lines of your code will be skipped as the debugger will not have had enough time to connect before they are being executed. You can work around this by refreshing the page or setting a timeout before executing the code in development mode.

Further reading

If you want to dig deeper in the debugging area, the following guides provide more information:

Resumen

Las aplicaciones Electron están configuradas usando paquetes npm. El ejecutable de Electron debe ser instalado en devDependencies de su proyecto y puede ejecutarse en modo de desarrollo usando un script en su archivo package.json.

El ejecutable ejecuta el punto de entrada JavaScript encontrado en la propiedad main de tu package.json. Este script controla el proceso principal, que ejecuta una instancia de Node.js. Y es responsable de controlar el ciclo de vida de su aplicación, mostrando interfaces nativas, realizando operaciones privilegiadas y gestionando procesos de renderizado.

Procesos de renderizado (o renderers para abreviar) son responsables de mostrar el contenido gráfico. Puedes cargar una página web en un renderer apuntándolo a una dirección web o a un archivo HTML local. Los renderizadores se comportan de forma muy similar a las páginas web habituales y tienen acceso a las mismas API web.

En la siguiente sección del tutorial, aprenderemos cómo aumentar el proceso de renderizado con APIs privilegiadas y cómo comunicarnos entre procesos.