Saltar al contenido principal

Modelo de proceso

Electron hereda su arquitectura multiprocesos de Chromium, lo que hace que la arquitectura del framework sea muy similar a la de un navegador web moderno. This guide will expand on the concepts applied in the Tutorial.

¿Por qué no un proceso único?

Los navegadores web son aplicaciones increíblemente complejas. Aparte de su capacidad principal para mostrar el contenido Web, tienen muchas responsabilidades secundarias, como administrar múltiples ventanas (o pestañas) y cargar extensiones de terceros.

En los comienzos, los navegadores usaban un solo proceso para todas estas funcionalidades. A pesar de que este patrón significaba menos sobrecarga para cada pestaña que estaba abierta, también significaba que un sitio web que se colgaba o dejaba de funcionar afectaba a todo el navegador.

El modelo multiproceso

Para resolver este problema, el equipo de Chrome decidió que cada pestaña se renderizaría en su propio proceso, limitando el daño que código malicioso o con errores de una página web puede causar a la aplicación en su conjunto. Un único proceso de navegador controla estos procesos, así como el ciclo de vida de la aplicación en su conjunto. El siguiente diagrama de Chrome Comic representa este modelo:

Chrome's multi-process architecture

Las aplicaciones que usan Electron están estructuradas de forma muy similar. As an app developer, you control two types of processes: main and renderer. Estos son análogos a los procesos de renderizado y de navegador del propio Chrome descritos anteriormente.

El proceso principal

Cada aplicación de Electron tiene un único proceso principal, el cual actúa como el punto de entrada de la aplicación. El proceso principal de se ejecuta en un entorno de Node.js, lo que se traduce en la posibilidad de require (requerir, importar) y usar todos los módulos de Node.js.

Window management

The main process' primary purpose is to create and manage application windows with the BrowserWindow module.

Each instance of the BrowserWindow class creates an application window that loads a web page in a separate renderer process. You can interact with this web content from the main process using the window's webContents object.

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

const win = new BrowserWindow({ width: 800, height: 1500 })
win.loadURL('https://github.com')

const contents = win.webContents
console.log(contents)

Note: A renderer process is also created for web embeds such as the BrowserView module. The webContents object is also accessible for embedded web content.

Debido a que e el módulo BrowserWindow es un EventEmitter, también puede añadir manejadores para varios eventos del usuario (por ejemplo, minimizando o maximizando la ventana).

When a BrowserWindow instance is destroyed, its corresponding renderer process gets terminated as well.

Application lifecycle

The main process also controls your application's lifecycle through Electron's app module. This module provides a large set of events and methods that you can use to add custom application behavior (for instance, programmatically quitting your application, modifying the application dock, or showing an About panel).

As a practical example, the app shown in the quick start guide uses app APIs to create a more native application window experience.

main.js
// quitting the app when no windows are open on non-macOS platforms
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})

APIs nativas

Para extender las características de Electron más allá de ser una envoltura de Chromium para contenido web, el proceso principal de también añade API personalizadas para interactuar con el sistema operativo del usuario. Electron exposes various modules that control native desktop functionality, such as menus, dialogs, and tray icons.

For a full list of Electron's main process modules, check out our API documentation.

The renderer process

Each Electron app spawns a separate renderer process for each open BrowserWindow (and each web embed). As its name implies, a renderer is responsible for rendering web content. For all intents and purposes, code ran in renderer processes should behave according to web standards (insofar as Chromium does, at least).

Therefore, all user interfaces and app functionality within a single browser window should be written with the same tools and paradigms that you use on the web.

Although explaining every web spec is out of scope for this guide, the bare minimum to understand is:

  • An HTML file is your entry point for the renderer process.
  • UI styling is added through Cascading Style Sheets (CSS).
  • Executable JavaScript code can be added through <script> elements.

Moreover, this also means that the renderer has no direct access to require or other Node.js APIs. In order to directly include NPM modules in the renderer, you must use the same bundler toolchains (for example, webpack or parcel) that you use on the web.

Renderer processes can be spawned with a full Node.js environment for ease of development. Historically, this used to be the default, but this feature was disabled for security reasons.

At this point, you might be wondering how your renderer process user interfaces can interact with Node.js and Electron's native desktop functionality if these features are only accessible from the main process. In fact, there is no direct way to import Electron's content scripts.

Preload scripts

Los scripts de precarga contienen el código que se ejecuta en un proceso de renderizado antes de que su contenido web comience a cargar. Estos scripts se ejecutan dentro del contexto del renderizador, pero tienen más privilegios al poder acceder a las APIs de Node.js.

A preload script can be attached to the main process in the BrowserWindow constructor's webPreferences option.

main.js
const { BrowserWindow } = require('electron')
// ...
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js'
}
})
// ...

Because the preload script shares a global Window interface with the renderers and can access Node.js APIs, it serves to enhance your renderer by exposing arbitrary APIs in the window global that your web contents can then consume.

Although preload scripts share a window global with the renderer they're attached to, you cannot directly attach any variables from the preload script to window because of the contextIsolation default.

preload.js
window.myAPI = {
desktop: true
}
renderer.js
console.log(window.myAPI)
// => undefined

Context Isolation means that preload scripts are isolated from the renderer's main world to avoid leaking any privileged APIs into your web content's code.

Instead, use the contextBridge module to accomplish this securely:

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

contextBridge.exposeInMainWorld('myAPI', {
desktop: true
})
renderer.js
console.log(window.myAPI)
// => { desktop: true }

This feature is incredibly useful for two main purposes:

  • By exposing ipcRenderer helpers to the renderer, you can use inter-process communication (IPC) to trigger main process tasks from the renderer (and vice-versa).
  • If you're developing an Electron wrapper for an existing web app hosted on a remote URL, you can add custom properties onto the renderer's window global that can be used for desktop-only logic on the web client's side.

The utility process

Each Electron app can spawn multiple child processes from the main process using the UtilityProcess API. The utility process runs in a Node.js environment, meaning it has the ability to require modules and use all of Node.js APIs. The utility process can be used to host for example: untrusted services, CPU intensive tasks or crash prone components which would have previously been hosted in the main process or process spawned with Node.js child_process.fork API. The primary difference between the utility process and process spawned by Node.js child_process module is that the utility process can establish a communication channel with a renderer process using MessagePorts. An Electron app can always prefer the UtilityProcess API over Node.js child_process.fork API when there is need to fork a child process from the main process.

Process-specific module aliases (TypeScript)

Electron's npm package also exports subpaths that contain a subset of Electron's TypeScript type definitions.

  • electron/main includes types for all main process modules.
  • electron/renderer includes types for all renderer process modules.
  • electron/common includes types for modules that can run in main and renderer processes.

These aliases have no impact on runtime, but can be used for typechecking and autocomplete.

Usage example
const { app } = require('electron/main')
const { shell } = require('electron/common')