Aller au contenu principal

Modèle de processus

Electron hérite son architecture multi-processus de Chromium, ce qui rend le framework très similaire dans son architecture à un navigateur web moderne. Ce guide va s'étendre sur les concepts appliqués dans le Tutoriel.

Et pourquoi pas un seul processus ?

Les navigateurs Web sont des applications incroyablement compliquées. Outre leur fonction principale d 'affichage de contenu Web, ils ont de nombreuses fonctionnalités secondaires, telles que la gestion de plusieurs fenêtres (ou onglets) et le chargement d’extensions tierces.

Dans les premiers temps, les navigateurs utilisaient généralement un seul processus pour toutes ces fonctionnalités . Bien que ce modèle signifiait moins d'usage de ressource pour chaque onglet ouvert, cela signifiait également que lorsqu'un site Web se plantait cela affectait l'ensemble du navigateur.

Le modèle multi-processus

Pour résoudre ce problème, l'équipe de Chrome a décidé que chaque onglet serait rendu dans son propre processus , limitant ainsi les dommages qu'un code bogué ou malveillant d'une page web pourrait causer à l'application dans son ensemble. Un seul processus de navigateur contrôle ensuite ces processus ainsi que que le cycle de vie de l'application dans son ensemble. Le diagramme ci-dessous issu de la Bd Chrome illustre ce modèle :

Chrome's multi-process architecture

Les applications Electron sont structurées de manière très similaire. En tant que développeur d’applications, vous contrôlez deux types de processus : principal et de rendu. Ceux-ci sont analogues aux propres processus de navigateur et de rendu de Chrome décrits ci-dessus.

Le processus principal

Chaque application Electron ne possède qu'un seul processus principal, celui-ci sert de point d'entrée à l'application. Le processus principal s'exécute dans un environnement Node.js, ce qui signifie qu'il a la capacité d'ajouter des modules à l'aide de require et d'utiliser toutes les API Node.js.

Gestion des fenêtres

Le rôle du processus principal est de créer et de gérer les fenêtres d'application avec le module BrowserWindow.

Chaque instance de la classe BrowserWindow crée une fenêtre d’application qui charge une page Web dans un processus de rendu distinct. Vous pouvez interagir avec ce contenu Web à partir du processus principal à l’aide de l’objet webContents de la fenêtre.

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)

Remarque : Un processus de rendu est également créé pour tout ce qui intègre du contenu web comme par exemple le module BrowserView. Et dans de tels cas l’objet webContents est également accessible.

Étant donné que le module BrowserWindow est du type EventEmitter, vous pouvez tout à fait ajouter des gestionnaires pour divers événements utilisateur (par exemple, lors de la réduction ou de l'agrandissement une fenêtre).

Lors de la destruction d’une instance de BrowserWindow, le processus de rendu correspondant est arrêté.

Cycle de vie de l’application

Le processus principal contrôle également le cycle de vie de votre application à travers le module app d'Electron. 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).

À titre d’exemple pratique, l’application présentée dans le guide de Démarrage rapide utilise les APIs de app pour obtenir un aspect plus natif des fenêtre de l’application.

main.js
//quitter l’application lorsqu’aucune fenêtre n’est ouverte sur les plates-formes non macOS
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})

API natives

Pour étendre les fonctionnalités d’Electron au-delà de celles d'un simple wrapper de Chromium pour du contenu Web, le processus principal ajoute également des API personnalisées pour interagir avec le système d’exploitation de l’utilisateur. Electron expose différents modules qui contrôlent les fonctionnalités de bureau natives, telles que les menus, les boîtes de dialogue et les icônes de la barre de tâches.

Pour une liste complète des modules du processus principal d’Electron, consultez notre documentation de l'API.

Le processus de rendu

Chaque application Electron génère un processus de rendu distinct pour toute BrowserWindow ouverte (ainsi que pour chaque contenu web intégré). Comme son nom l'indique, un moteur de rendu (renderer) est responsable du rendu de contenu web. A toutes fins utiles, le code exécuté dans les processus de rendu devrait se comporter selon les standards web (au moins de la même façon que Chromium).

Par conséquent, toutes les interfaces utilisateur et fonctionnalités de l'application au sein d'une fenêtre seront écrites avec les mêmes outils et paradigmes que vous utilisez pour le Web .

Bien que l'explication de chaque spécification web soit hors de la portée de ce guide, les points suivants représentent le minimum vital pour comprendre:

  • Votre point d'entrée pour le processus de rendu est un fichier HTML.
  • Le style de l'interface utilisateur est ajouté via des feuilles de style (CSS).
  • Le code JavaScript exécutable peut être ajouté via des éléments <script> .

De plus, cela signifie également que le moteur de rendu n’a pas d’accès direct à require ou à d’autres API Node.js. Pour inclure directement des modules NPM dans le moteur de rendu, vous devez utiliser les mêmes chaînes d’outils de groupage (par exemple, webpack ou parcel) que ceux utilisés pour le Web.

danger

Les processus de rendu peuvent être générés avec un environnement Node.js complet pour faciliter le développement . Historiquement, c'était d'ailleurs la valeur par défaut, mais cette fonctionnalité a été désactivée pour des raisons de sécurité.

À ce stade, vous pouvez vous demander comment vos interfaces utilisateur de processus de rendu peuvent interagir avec Node.js et les fonctionnalités natives d'Electron si ces fonctionnalités ne sont accessibles que depuis le processus principal. En fait, il n'y a pas de moyen direct d'importer des scripts de contenu d'Electron.

Scripts de préchargement

Les scripts de préchargement (preload) contiennent du code qui s'exécute dans un processus de rendu (renderer process) avant que son contenu web ne se charge. Ces scripts s’exécutent dans le contexte du moteur de rendu, mais ont des privilèges supplémentaires qui leur donnent accès aux API Node.js.

Un script de préchargement peut être attaché au processus principal à l'aide de l’option webPreferences du constructeur de BrowserWindow .

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

Comme le script de préchargement partage une interface Window globale avec les moteurs de rendu et peut accéder aux API Node.js, il va être utilisé pour enrichir votre moteur de rendu en exposant des API diverses dans le window global qui seront utilsables ensuite par votre contenu Web.

Bien que les scripts de préchargement partagent un window global avec le moteur de rendu auquel ils sont attachés, vous ne pouvez pas attacher directement des variables du script de préchargement à window en raison de la valeur par défaut de contextIsolation .

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

L'isolement du contexte (contextIsolation) implique que les scripts de préchargement sont isolés du monde principal(mainworld) du moteur de rendu pour éviter toute brêche donnant accès à des API privilégiées dans le code de votre contenu web.

Vous devrez donc, utiliser plutôt le module contextBridge qui vous permettra d'être en sécurité :

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

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

Cette fonctionnalité est incroyablement utile pour deux raisons principales:

  • En exposant des "helpers" ipcRenderer au moteur de rendu, vous pouvez utiliser la communication inter-processus (IPC) pour déclencher des tâches du processus principalesà partir d'un moteur de rendu (et vice versa).
  • Si vous développez un wrapper Electron pour une application web existante hébergée sur une URL distante, vous pouvez ajouter des propriétés personnalisées au window global du moteur de rendu pouvant être utilisées uniquement du côté du client Web de l'appli de bureau.

Utility-process

Chaque application Electron peut faire apparaître plusieurs processus fils depuis le processus principal en utilisant l'API UtilityProcess. Un processus utilitaire s'exécute dans un environnement Node.js, ce qui signifie qu'il a la capacité d'inclure des modules par require et d'utiliser toutes les API Node.js. Un tel processus peut être utilisé pour héberger par exemple : des services non approuvés, des tâches intensives en CPU ou des composants susceptibles de planter qui auraient été précédemment hébergés dans le processus principal ou un processus créé avec l'API de Node.js child_process.fork. La différence principale entre un processus utilitaire et un processus créé par le module child_process de Node.js, est que l'utilitaire peut établir un canal de communication avec un processus de rendu en utilisant MessagePort. Une application Electron privilégiera toujours l'API UtilityProcess à child_process.fork quand elle nécessite un fork d'un processus fils du processus principal.

Alias du module spécifiques au processus (TypeScript)

Le package npm d'Electron exporte également des sous-chemins qui contiennent un sous-ensemble de définitions de type TypeScript d'Electron.

  • electron/main inclut les types pour tous les modules du processus principal.
  • electron/renderer inclut les types pour tous les modules de processus de rendu.
  • electron/common inclut les types de modules pouvant être exécutés dans les processus principaux et de rendu.

Ces alias n'ont aucun impact sur l'exécution, mais peuvent être utilisés pour le controle des types ainsi que l'auto-complétion.

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