Construye tu primera aplicación
Esta es la parte 2 del tutorial de Electró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
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
.
- npm
- Yarn
mkdir my-electron-app && cd my-electron-app
npm init
mkdir my-electron-app && cd my-electron-app
yarn 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). - author, license, and description can be any value, but will be necessary for packaging later on.
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.
Esto puede parecer contradictorio, ya que tu código de producción está utilizando las APIs de Electron. 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
- Yarn
npm install electron --save-dev
yarn add electron --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.
{
"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"
}
}
If installing Electron directly fails, please refer to our Advanced Installation documentation for instructions on download mirrors, proxies, and troubleshooting steps.
Agregando un .gitignore
El archivo .gitignore
especifica qué archivos y directorios evitar rastrear con 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
Read Electron's process model documentation to better understand how Electron's multiple processes work together.
:::
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:
console.log('Hello from Electron 👋')
Because Electron's main process is a Node.js runtime, you can execute arbitrary Node.js code with the electron
command (you can even use it as a 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.
{
"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
- Yarn
npm run start
yarn 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:
<!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>
Now that you have a web page, you can load it into an Electron BrowserWindow. Reemplaza el contenido de tu archivo main.js
por el siguiente código. Explicaremos cada bloque resaltado por separado.
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
const { app, BrowserWindow } = require('electron')
En la primera línea, estamos importando dos módulos Electron con la sintaxis del módulo CommonJS:
- app, which controls your application's event lifecycle.
- BrowserWindow, which creates and manages app windows.
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')
For more information, see the Process Model docs.
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:
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
Llamar a tu función cuando la app esté lista
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.
In Electron, BrowserWindows can only be created after the app module's ready
event is fired. You can wait for this event by using the app.whenReady()
API and calling createWindow()
once its promise is fulfilled.
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.
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. To implement this pattern in your Electron app, listen for the app module's window-all-closed
event, and call app.quit()
to exit your app if the user is not on 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.
To implement this feature, listen for the app module's activate
event, and call your existing createWindow()
method if no BrowserWindows are open.
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()
})
})
Código inicial final
- main.js
- index.html
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()
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<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>
<p id="info"></p>
</body>
<script src="./renderer.js"></script>
</html>
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:
{
"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"
}
]
}
La opción "Main + renderer" aparecerá cuando selecciones "Run and Debug" desde la barra lateral, permitiéndote establecer puntos de interrupción e inspeccionar todas las variables, entre otras cosas, tanto en el proceso principal como en el de renderizado.
Lo que hemos hecho en el archivo launch.json
es crear 3 configuraciones:
Main
se usa para iniciar el proceso principal y también exponer el puerto 9222 para depuración remota (--remote-debugging-port=9222
). Este es el puerto que utilizaremos para adjuntar el depurador para elRenderer
. Dado que el proceso principal es un proceso de Node.js, el tipo se establece ennode
.Renderer
se usa para depurar el proceso de renderizado. Dado que el proceso principal es el que crea el proceso, debemos "adjuntarnos" a él ("request": "attach"
) en lugar de crear uno nuevo. El proceso de renderizado es un proceso web, por lo que el depurador que debemos usar eschrome
.Main + renderer
es una tarea compuesta que ejecuta las anteriores simultáneamente.
Debido a que nos estamos adjuntando a un proceso en Renderer
, es posible que las primeras líneas de tu código se omitan, ya que el depurador puede no haber tenido suficiente tiempo para conectarse antes de que se ejecuten. Puedes solucionar esto actualizando la página o configurando un tiempo de espera antes de ejecutar el código en modo de desarrollo.
Si deseas profundizar en el área de depuración, las siguientes guías proporcionan más información:
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.