03/ ).replace( /^(0[1-9]|1[0-2])$/g, '$1/' // 11 > 11/ ).replace( /^1([3-9])$/g, '01/$1' // 13 > 01/3 //UPDATED by NAVNEET // ).replace( // /^(0?[1-9]|1[0-2])([0-9]{2})$/g, '$1/$2' // 141 > 01/41 ).replace( /^0\/|0+$/g, '0' // 0/ > 0 and 00 > 0 //UPDATED by NAVNEET ).replace( /[^\d|^\/]*/g, '' // To allow only digits and `/` //UPDATED by NAVNEET ).replace( /\/\//g, '/' // Prevent entering more than 1 `/` ); } ``` > Nota: Al observar este documento Html, puede observar que los números de versión > están ausentes en el texto del body. Los insertaremos manualmente más tarde usando JavaScript. ### Abriendo tu página web en una ventana del navegador Ahora que tienes una página web, cárgala en una ventana de la aplicación. Para hacerlo, necesitarás dos módulos de Electron: * El módulo [`app`][app], que controla el ciclo de vida de eventos de tu aplicación. * El módulo [`BrowserWindow`][browser-window], que crea y administra las ventanas de la aplicación. Ya que el proceso principal ejecuta Node. js, puede importarlos como módulos [CommonJS][commonjs] en la parte superior de su archivo: ```js const { app, BrowserWindow} = require('electron') ``` A continuación, añade una función `createWindow()` que cargue `index. html` en una nueva instancia de `BrowserWindow`. ```js const createWindow = () => { const win = new BrowserWindow({ width: 800, height: 600 }) win. loadFile('index. html')} ``` A continuación, llama a esta función `createWindow()` para abrir tu ventana. En Electron, las ventanas del navegador sólo se pueden crear después de que se dispare el evento [`ready`][app-ready] del módulo `app` Puedes esperar a este evento utilizando la Api [`app. whenReady()`][app-when-ready]. Llama a `createWindow()` después de que `whenReady()` resuelva su Promise. ```js app. whenReady(). then(() => { createWindow()}) ``` > Nota: Llegados a este punto, su aplicación Electron debería abrir correctamente > una ventana que muestre su página web. [app]:../api/app. md [browser-window]:../api/browser-window. md [commonjs]: https://nodejs. org/docs/latest/api/modules. html#modules_modules_commonjs_modules [app-ready]:../api/app. md#event-ready [app-when-ready]:../api/app. md#appwhenready ### Gestione el ciclo de vida de sus ventanas Aunque ahora se puede abrir una ventana del navegador, necesitará un poco de código boilerplate adicional para que se sienta más nativo a cada plataforma. Las ventanas de aplicación se comportan de forma diferente en cada Os, y Electron pone la responsabilidad en los desarrolladores para aplicar estas convenciones en su aplicación. En general, puedes utilizar el atributo [`platform`][node-platform] del global `process` para ejecutar código específico para determinados sistemas operativos. #### Salir de la aplicación cuando se cierran todas las ventanas (Windows & Linux) En Windows y Linux, salir de todas las ventanas generalmente cierra una aplicación por completo. Para implementar esto, escucha el evento [`'window-all-closed'`][window-all-closed] del módulo `app` y llama a [`app. quit()`][app-quit] si el usuario no está en macOS (`darwin`). ```js app. on('window-all-closed', () => { if (process. platform!== 'darwin') app. quit()}) ``` [node-platform]: https://nodejs. org/api/process. html#process_process_platform [window-all-closed]:../api/app. md#event-window-all-closed [app-quit]:../api/app. md#appquit #### Abrir una ventana si no hay ninguna abierta (macOS) Mientras que las aplicaciones de Linux y Windows se cierran cuando no tienen ninguna ventana abierta, las de macOS generalmente continúan ejecutándose incluso sin ninguna ventana abierta, y activar la aplicación cuando no hay ventanas cuando no hay ventanas disponibles debería abrir una nueva. Para implementar esta característica, escucha el evento [`activate`][activate] del módulo `app` y llama al método `createWindow()` existente si no hay ninguna ventana abierta. Debido a que las ventanas no se pueden crear antes del evento `ready`, sólo debes escuchar los eventos `activate` después de que tu aplicación se haya inicializado. Para ello, adjunta tu evento listener dentro de la llamada de retorno existente `whenReady()`. [activate]:../api/app. md#event-activate-macos ```js app. whenReady(). then(() => { createWindow() app. on('activate', () => { if (BrowserWindow. getAllWindows(). length === 0) createWindow() })}) ``` > Nota: Llegados a este punto, los controles de las ventanas deberían ser totalmente funcionales. ### Accede a Node. js desde el renderizador con un script de precarga Ahora, lo último que hay que hacer es imprimir los números de versión de Electron y sus dependencias en su página web. Acceder a esta información es trivial en el proceso principal a través del objeto global de Node `process`. Sin embargo, no puedes editar el Dom desde el proceso principal porque no tiene acceso al contexto `document` del renderizador. Están en procesos completamente diferentes. > Nota: Si necesita profundizar más en los procesos Electron, consulte el documento > [Modelo de proceso][Process Model]. Aquí es donde resulta útil adjuntar un **script de precarga** al renderizador. Un script de precarga se ejecuta antes de que el proceso de renderizado se cargue, y tiene acceso tanto a globales del renderizador (ej. `window` y `document`) y al entorno Node. js. Crea un nuevo script llamado `preload. js` como tal: ```js window. addEventListener('DomcontentLoaded', () => { const replaceText = (selector, text) => { const element = document. getElementById(selector) if (element) element. innerText = text } for (const dependency of ['chrome', 'node', 'electron']) { replaceText(`${dependency}-version`, process. versions[dependency]) }}) ``` El código anterior accede al objeto Node. js `process. versions` y ejecuta una función helper básica `replaceText` para insertar los números de versión en el documento Html. Para adjuntar este script a tu proceso de renderizado, pasa la ruta a tu script de precarga la opción `webPreferences. preload` de su constructor `BrowserWindow`. ```js // incluye el módulo 'path' de Node. js al principio de tu archivo const path = require('path') // modifica la función createWindow() existente const createWindow = () => { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path. join(__dirname, 'preload. js') } }) win. loadFile('index. html')} //... ``` Aquí se utilizan dos conceptos de Node. js: * La cadena [`__dirname`][dirname] apunta a la ruta del script actualmente en ejecución (en este caso, la carpeta raíz del proyecto). * La Api [`path. join`][path-join] une múltiples segmentos de ruta, creando una cadena de ruta combinada que funciona en todas las plataformas. Utilizamos una ruta relativa al archivo JavaScript que se está ejecutando en ese momento para que su ruta relativa funcione tanto en el modo de desarrollo como en el modo empaquetado. [Process Model]:./process-model. md [dirname]: https://nodejs. org/api/modules. html#modules_dirname [path-join]: https://nodejs. org/api/path. html#path_path_join_paths ### Bonus: Añada funcionalidad a sus contenidos web Llegados a este punto, puede que te estés preguntando cómo añadir más funcionalidad a tu aplicación. Para cualquier interacción con los contenidos de su web, querrá añadir scripts a su proceso de renderizado. Como el renderizador se ejecuta en un entorno web normal, puede añadir una etiqueta ` ``` El código contenido en `renderer. js` puede entonces utilizar las mismas Apis de JavaScript y herramientas que usas para el típico desarrollo front-end, como usar [`webpack`][webpack] para empaquetar y minificar tu código o [React][react] para gestionar tus interfaces de usuario. [webpack]: https://webpack. js. org [react]: https://reactjs. org ### Recapitulemos Después de seguir los pasos anteriores, debería tener una aplicación Electron completamente funcional:![La aplicación Electron más sencilla](../images/simplest-electron-app. png) El código completo está disponible a continuación: ```js // main. js // Módulos para controlar la vida de la aplicación y crear una ventana de navegación nativa const { app, BrowserWindow} = require('electron') const path = require('path') const createWindow = () => { // Crea la ventana del navegador. const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path. join(__dirname, 'preload. js') } }) // y carga el index. html de la aplicación. mainWindow. loadFile('index. html') // Abra el DevTools. // mainWindow. webContents. openDevTools()} // Este método será llamado cuando Electron haya terminado // inicialización y está listo para crear ventanas de navegador. // Algunas Apis sólo se pueden utilizar después de que se produzca este evento. app. whenReady(). then(() => { createWindow() app. on('activate', () => { // En macOS es común volver a crear una ventana en la aplicación cuando al // icono del dock de les click y no hay otras ventanas abiertas. if (BrowserWindow. getAllWindows(). length === 0) createWindow() })}) // Salir cuando todas las ventanas están cerradas, excepto en macOS. Allí, es común // que las aplicaciones y su barra de menú permanezcan activas hasta que el usuario las cierre // explícitamente con Cmd + Q. app. on('window-all-closed', () => { if (process. platform!== 'darwin') app. quit()}) // En este archivo puedes incluir el resto del proceso principal específico de tu app // También puedes ponerlos en archivos separados y requerirlos aquí. ``` ```js // preload. js // Todas las Api de Node. js están disponibles en el proceso de precarga. // Tiene el mismo sandbox que una extensión de Chrome. window. addEventListener('DomcontentLoaded', () => { const replaceText = (selector, text) => { const element = document. getElementById(selector) if (element) element. innerText = text } for (const dependency of ['chrome', 'node', 'electron']) { replaceText(`${dependency}-version`, process. versions[dependency]) }}) ``` ```html Hello World!

Hello World!

We are using Node. js , Chromium , and Electron . ``` ```fiddle docs/latest/fiddles/quick-start ``` Para resumir todos los pasos que hemos dado: * Arrancamos una aplicación Node. js y añadimos Electron como dependencia. * Creamos un script `main. js` que ejecuta nuestro proceso principal, que controla nuestra app y se ejecuta en un entorno Node. js. En este script, utilizamos los módulos `app` y `BrowserWindow` de Electron para crear una ventana de navegador que muestre el contenido web en un proceso separado (el renderizador). * Para acceder a ciertas funcionalidades de Node. js en el renderizador, adjuntamos un script de precarga a nuestro constructor `BrowserWindow`. ## Empaquete y distribuya su aplicación La forma más rápida de distribuir su aplicación recién creada es utilizar [Electron Forge](https://www. electronforge. io). 1. Añade Electron Forge como una dependencia de desarrollo de tu aplicación, y utiliza su comando `import` para configurar Forge: ```sh npm2yarn npm install --save-dev @electron-forge/cli npx electron-forge import ✔ Checking your system ✔ Initializing Git Repository ✔ Writing modified package. json file ✔ Installing dependencies ✔ Writing modified package. json file ✔ Fixing. gitignore We have Attempted to convert your app to be in a format that electron-forge understands. Thanks for using "electron-forge"!!! ``` 2. Crea un distribuible utilizando el comando `make` de Forge: ```sh npm2yarn npm run make > my-electron-app@1.0.0 make /my-electron-app > electron-forge make ✔ Checking your system ✔ Resolving Forge Config We need to package your application before we can make it ✔ Preparing to Package Application for arch: x64 ✔ Preparing native dependencies ✔ Packaging Application Making for the following targets: zip ✔ Making for target: zip - On platform: darwin - For arch: x64 ``` Electron Forge crea la carpeta `out` donde se ubicará su paquete: ```plain // Example for macOS out/ ├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0. zip ├──... └── out/my-electron-app-darwin-x64/my-electron-app. app/Contents/MacOS/my-electron-app ```.'>
Saltar al contenido principal

Using Preload Scripts

Objetivos de aprendizaje

En esta parte del tutorial, aprenderás qué es un script de precarga y cómo utilizar una para exponer de forma segura las API privilegiadas en el proceso de renderizado. También aprenderá cómo comunicarse entre los procesos principales y de representación con los módulos de comunicación entre procesos (IPC) de Electron.

¿Qué es un script de precarga?

El proceso principal de Electron es un entorno Node.js que tiene acceso completo al sistema operativo. On top of Electron modules, you can also access Node.js built-ins, as well as any packages installed via npm. Por otro lado, los procesos renderizadores ejecutan páginas web y no ejecutan Node.js por defecto por razones de seguridad.

Para unir los diferentes tipos de proceso de Electron, necesitaremos usar un script especial llamado precarga.

Aumentar el renderizador con un script de precarga

Un script de precarga de BrowserWindows se ejecuta en un contexto que tiene acceso tanto al DOM del HTML como a un subconjunto limitado de Node.js y APIs de Electron.

Pre-cargar script sandboxing

A partir de Electron 20 en adelante, los scripts de precarga son sandboxed por defecto y ya no tienen acceso a un entorno Node.js completo. En la práctica, esto significa que tiene una función de solicitud (require) policompletada (Polyfilled) la cual solo tiene acceso a un conjunto limitado de API.

APIs disponiblesDetalles
Módulos ElectronMódulos de Process Renderer
Módulos Node.jsevents, timers, url
Globales PolyfilledBuffer, process, clearImmediate, setImmediate

For more information, check out the Process Sandboxing guide.

Los scripts de precarga son inyectados antes de que una página web cargue en el renderizador, similar a los de una extensión de Chrome scripts de contenido. To add features to your renderer that require privileged access, you can define global objects through the contextBridge API.

Para demostrar este concepto, crearás un script de precarga que expone las versiones de Chrome, Node y Electron en el renderizador.

Añade un nuevo script preload.js que expone las propiedades seleccionadas del objeto process.versions de Electron al proceso de renderizado en una variable global versions.

preload.js
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron
// también podemos exponer variables, no solo funciones
})

Para adjuntar este script a tu proceso de renderizado, proporciona la ruta a tu script de precarga a la opción webPreferences.preload en su constructor existente.

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

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

win.loadFile('index.html')
}

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

There are two Node.js concepts that are used here:

  • La cadena __dirname apunta a la ruta del script actualmente en ejecución (en este caso, la carpeta raíz de tu proyecto).
  • La API path.join une varios segmentos de rutas juntos, creando una cadena de ruta combinada que funciona en todas las plataformas.

En este punto, el renderizador tiene acceso de las versions globales, así que vamos a mostrar esa información en la ventana. Se puede acceder a esta variable a través de window.versions o simplemente versions. Crea un script renderer.js que utilice la document.getElementById del DOM de la API para reemplazar el texto mostrado para el elemento HTML con info como su propiedad id.

renderer.js
const information = document.getElementById('info')
information.innerText = `Esta aplicación está usando Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`

Luego, modifica tu index.html agregando un nuevo elemento con info como su propiedad id, y adjunta tu script renderer.js:

index.html
<!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>¡Hola desde el renderizador Electron!</title>
</head>
<body>
<h1>¡Hola desde el renderizador Electron!</h1>
<p>👋</p>
<p id="info"></p>
</body>
<script src="./renderer.js"></script>
</html>

Después de seguir los pasos anteriores, tu aplicación debería ser algo así:

Aplicación Electron que muestra esta aplicación está usando Chrome (v102.0.5005.63), Node.js (v16.14.2), y Electron (v19.0.3)

Y el código debe verse así:

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

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

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()
}
})

Comunicación entre procesos

Como hemos mencionado anteriormente, el proceso principal y renderizador de Electron tiene distintas responsabilidades y no son intercambiables. Esto significa que no es posible acceder al Node.js APIs directamente del proceso de renderizado, ni del Objeto de Documento HTML (DOM) del proceso principal.

La solución para este problema es utilizar los módulos ipcMain y ipcRenderer de Electron para la comunicación entre procesos (IPC). Para enviar un mensaje desde su página web al proceso principal, puede configurar un controlador de proceso principal con ipcMain.handle y luego exponer una función que llame a ipcRenderer.invoke para active el controlador en su secuencia de comandos de precarga.

Para ilustrar, añadiremos una función global al renderizador llamado ping() que devolverá una cadena del proceso principal.

Primero, configura la llamada invoke en tu script de precarga:

preload.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
ping: () => ipcRenderer.invoke('ping')
// también podemos exponer variables, no solo funciones
})
seguridad IPC

Observa cómo envolver la llamada ipcRenderer.invoke('ping') en una función ayudante en lugar de que exponer el módulo ipcRenderer directamente a través del puente contextual. Usted nunca desea exponer directamente todo el módulo ipcRenderer a través de la precarga. Esto daría a su renderizador la capacidad de enviar mensajes IPC arbitrarios al proceso principal, que se convierte en un poderoso vector de ataque para código malicioso.

Luego, configura tu oyente handle en el proceso principal. Hacemos esto antes de cargar el archivo HTML para garantizar que el controlador esté listo antes de enviar la llamada invoke desde el renderizador.

main.js
const { app, BrowserWindow, ipcMain } = require('electron/main')
const path = require('node:path')

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
ipcMain.handle('ping', () => 'pong')
createWindow()
})

Una vez que tengas configurado el remitente y el receptor, ahora puedes enviar mensajes desde el renderizador al proceso principal a través del canal 'ping' que acabas de definir.

renderer.js
const func = async () => {
const response = await window.versions.ping()
console.log(response) // prints out 'pong'
}

func()
info

For more in-depth explanations on using the ipcRenderer and ipcMain modules, check out the full Inter-Process Communication guide.

Resumen

Un script de precarga contiene el código que se ejecuta antes de que tu página web se cargue en la ventana del navegador. Tiene acceso tanto a las API de DOM como al entorno Node.js, y a menudo se utiliza para exponer APIs privilegiadas al renderizador a través de la API contextBridge.

Debido a que los procesos principal y de representación tienen responsabilidades muy diferentes, las aplicaciones de Electron a menudo usan el script de precarga para configurar interfaces de comunicación entre procesos (IPC) para pasar mensajes arbitrarios entre los dos tipos de procesos.

In the next part of the tutorial, we will be showing you resources on adding more functionality to your app, then teaching you how to distribute your app to users.