Aller au contenu principal

Créer votre première application

Objectifs

Dans cette partie du tutoriel, vous apprendrez comment configurer votre projet Electron et écrire une simple application pour débuter. À la fin de cette section, vous devriez être en mesure d'exécuter une application Electron fonctionnelle en mode développement depuis votre terminal.

Configuration de votre projet

Évitez WSL

Si vous utilisez un ordinateur Windows, n’utilisez pas le sous-système Windows pour Linux (WSL) lorsque vous suivez ce tutoriel, sinon vous rencontrerez des problèmes lors de l’exécution de l’application.

Initialisation de votre projet npm

Les applications Electron sont édifiées à l'aide de npm, avec le fichier package.json comme point d'entrée. Commencez par créer un dossier et exécutez npm init qui va créer et configurer le fichier package. json.

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

Cette commande vous demande alors certaines informations afin de configurer certains champs du fichier package.json. Quelques règles devront être suivies pour les besoins de ce tutoriel:

  • entry point doit être renseigné avec main.js (vous créerez ce fichier un peu plus loin).
  • author, license, et description peuvent prendre la valeur que vous souhaitez, mais seront nécessaires plus tard pour l' empaquetage.

Ensuite, installez Electron dans le devDependencies de votre application, qui est la liste des dépendances de modules externes nécessaires pour le développement et non requises en production.

Pourquoi mettre Electron dans les devDependencies ?

Cela peut sembler, au prime abord, contre-intuitif puisque votre code de production exécute des API Electron. Cependant, les applications empaquetées sont livrées avec le binaire Electron, ce qui élimine ainsi le besoin de le spécifier comme dépendance de production.

npm install electron --save-dev

Maintenant, après avoir initialisé votre fichier package.json et installé Electron votre fichier package.json devrait ressembler à ce qui suit. Vous devriez également avoir un dossier node_modules contenant l'exécutable d'Electron, ainsi qu'un fichier de verrouillage package-lock.json qui spécifie les versions exactes des dépendances à installer.

package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "19.0.0"
}
}
Procédure d’installation avancée d'Electron

Dans le cas ou l’installation directe d’Electron échoue, reportez-vous à notre documentation Installation avancée pour obtenir des instructions sur les miroirs de téléchargement, les proxys et les opérations de dépannage.

Ajout d'un .gitignore

Le fichier .gitignore indique à git quels fichiers et répertoires ne sont pas à surveiller. Vous devez placer une copie du modèle de gitignore pour Node.js dans le dossier racine de votre projet pour éviter de valider la surveillance du dossier node_modules de votre projet.

Exécution d'une application Electron

Lectures complémentaires

Lisez la documentation du modèle de processus d'Electron pour mieux comprendre comment les multiples processus d'Electron coopèrent.

Le script main que vous avez défini dans package.json est le point d’entrée de toute application Electron . Ce script contrôle le processus principal , qui s’exécute dans un environnement Node.js et est responsable du contrôle du cycle de vie de votre application, de l’affichage des interfaces natives, de l’exécution d’opérations privilégiées et de la gestion des processus de rendu (nous y reviendrons plus tard).

Avant de créer votre première application Electron, vous allez tout d’abord utiliser un script trivial afin de vous assurer que le point d’entrée du processus principal est correctement configuré. Vous allez donc pour cela créer un fichier main.js dans le dossier racine de votre projet avec une seule ligne de code :

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

Étant donné que le processus principal d’Electron est un runtime Node.js, vous pouvez exécuter tout code Node.js avec la commande electron (vous pouvez même l’utiliser comme REPL). Pour exécuter ce script, ajoutez electron . à la commande start dans le champ scripts de votre package.json. Cette commande dit à l'exécutable d'Electron de rechercher le script principal dans le répertoire courant et de l'exécuter en mode dev.

package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^19.0.0"
}
}
npm run start

Votre terminal devrait alors afficher Hello from Electron 👋. Félicitations, vous venez d'exécuter votre première ligne de code avec Electron! Maintenant , vous allez apprendre comment créer des interfaces utilisateur avec HTML et les charger dans une fenêtre native.

Chargement d’une page Web dans une BrowserWindow

Avec Electron, chaque fenêtre affiche une page Web qui peut être chargée à partir d'un fichier HTML local ou d'une adresse Web distante. Pour cet exemple, vous allez charger un fichier local. Commencez par créer un squelette de page web dans un fichier index.html à la racine de votre projet :

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>Bonjour depuis le rendu d'Electron !</title>
</head>
<body>
<h1>Bonjour depuis le rendu d'Electron !</h1>
<p>👋</p>
</body>
</html>

Maintenant que vous avez une page web, vous pouvez la charger dans une BrowserWindow d'Electron. Replace the contents of your main.js file with the following code. Nous expliquerons séparement chaque bloc surligné.

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

Importation de modules

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

Dans cette première ligne, nous importons deux modules Electron avec la syntaxe des module CommonJS :

  • Le module app, qui contrôle le cycle de vie des événements de votre application.
  • Le module BrowserWindow, qui crée et gère les fenêtres d’application.
Conventions pour les majuscules

Vous avez peut-être remarqué la différence de majuscule entre les modules de app et BrowserWindow. Electron suit ici les conventions JavaScript typiques, où les modules nommés en Pascal case sont des constructeurs de classes instanciables (par ex. BrowserWindow, Tray, Notification) alors que les modules nommés en Camel case ne sont pas instanciables (par exemple, app, ipcRenderer, webContents).

Modules ES dans Electron

Les modules ECMAScript (ceux que l'on charge à l'aide de import ) ne sont actuellement pas directement pris en charge avec Electron. Vous pouvez trouver plus d'informations au sujet de ces ESM dans Electron dans la description du problème electron/electron#21457 sur github.

Écriture d’une fonction réutilisable pour instancier des fenêtres

La fonction createWindow() charge votre page web dans une nouvelle instance de BrowserWindow :

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

win.loadFile('index.html')
}

Appel de votre fonction lorsque l’application est prête

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

De nombreux modules de base d’Electron sont des émetteurs d’événements Node.js qui adhèrent à l’architecture événementielle asynchrone de Node. Le module app est l’un de ces émetteurs.

Dans Electron, BrowserWindows ne peut être créé qu’après le déclenchement de l’événement ready du module d’application. Vous pouvez attendre cet événement en utilisant l'API app.whenReady() et en appelant createWindow() une fois sa promesse réalisée.

info

Typiquement, vous écoutez les événements Node.js à l’aide de la fonction .on d’un émetteur.

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

Toutefois, Electron expose la fonction helper app.whenReady() pour l’événement ready afin d’éviter les pièges subtils pouvant survenir lors de l'écoute directe de cet événement. Voir electron/electron#21972 sur github pour plus de détails.

Arrivé à ce stade, l’exécution du script start de votre application Electron devrait ouvrir avec succès une fenêtre qui affiche votre page Web!

Chaque page Web affichée par votre application dans une fenêtre s’exécutera dans un processus distinct appelé processus de rendu (ou simplement renderer ). Les processus de rendu ont accès aux mêmes API et outils JavaScript que ceux utilisés pour le développement front-end classique, tels que Webpack pour regrouper et minimiser votre code ou React pour créer vos interfaces utilisateur.

Gestion du cycle de vie des fenêtres de votre application

Les fenêtres d'une application se comportent différemment selon le système d’exploitation. Plutôt que d’appliquer ces conventions par défaut, Electron vous permet de choisir de les implémenter dans votre code si vous souhaitez les suivre . Vous pouvez implémenter ces conventions de de base en écoutant les événements émis par l’application et les modules BrowserWindow.

Flux de contrôle spécifique

La vérification de la variable process.platform de Node peut vous aider à d’exécuter conditionnellement du code sur certaines plates-formes. Notez qu'Electron ne peut fonctionner que sur trois plates-formes : win32 (Windows), linux (Linux), et darwin (macOS).

Quitter l'application lorsque toutes les fenêtres sont fermées (Windows & Linux)

Sur Windows et Linux, le fait de fermer toutes les fenêtres ferme généralement entièrement l'application. Pour implémenter ce modèle dans votre application Electron, écoutez l'événement window-all-closed du module app, et appelez app.quit() pour quitter votre application si l'utilisateur n'est pas sur macOS.

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

Ouverture d'une fenêtre si aucune n'est ouverte (macOS)

Par contre, les applications macOS continuent généralement à fonctionner même si aucune fenêtre n'est ouverte. L’activation de l’application lorsqu’aucune fenêtre n’est disponible va en ouvrir une nouvelle.

Pour implémenter cette fonctionnalité, écoutez l'événement activate du module app et appelez votre méthode createWindow() existante si aucune BrowserWindows n'est ouverte.

Étant donné que les fenêtres ne peuvent pas être créées avant l'événement ready , vous ne devrez écouter l'événement activate qu'après l'initialisation de votre application. Pour ce faire, écoutez uniquement les événements d’activation dans la callback de votre whenReady() existant.

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

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

Code final

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

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

Facultatif : Débogage avec VS Code

Si vous souhaitez déboguer votre application à l’aide de VS Code, vous devez attacher VS Code aux processus principal et de rendu. Nous allons voir maintenant une configuration prête à être utilisée. Vous devez pour cela créer une configuration launch.json dans le dossier .vscode de votre projet si celui-ci n'existe pas encore :

.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"
}
]
}

Suite à cela la nouvelle option "Main + renderer" apparaîtra lorsque vous sélectionnez "Run and Debug" dans la barre latérale, en l'utilisant vous pourrez entre autres définir des points d'arrêt et inspecter toutes les variables dans les processus principal et de rendu.

Ce que nous avons fait dans ce fichier launch.json est de créer 3 configurations :

  • Main qui est utilisée pour démarrer le processus principal et expose également le port 9222 pour le débogage distant (--remote-debugging-port=9222). C'est le port que nous utiliserons pour attacher le débogueur au Renderer. Puisque le processus principal est un processus Node.js, le type est défini à node.
  • Renderer, elle est utilisée pour déboguer le processus de rendu. Le processus principal étant celui qui crée ce processus, nous devons « nous y attacher » ("request": "attach") au lieu d'en créer un nouveau. Le processus de rendu est un processus web, donc le débogueur que nous devons utiliser est chrome.
  • Main + renderer est une tâche composée qui exécute les deux précédentes et ceci simultanément.
caution

Comme nous nous attachons à un processus dans Renderer, il est possible que les premières lignes de votre code soient ignorées si le débogueur n’a pas eu assez de temps pour se connecter avant qu’elles ne soient exécutées. Vous pouvez contourner ce problème en actualisant la page ou en définissant un délai avant l'exécution du code en mode développement.

Lectures complémentaires

Si vous souhaitez approfondir le sujet du débogage, les guides suivants vous fourniront d'avantage d'informations :

Récapitulatif

Les applications Electron sont configurées à l'aide de paquets npm. L'exécutable Electron doit être installé dans les devDependencies de votre projet et peut être exécuté en mode développement en utilisant un script de votre fichier package.json.

L’exécutable exécute le point d’entrée JavaScript indiqué par la propriété main de votre fichier package.json. Ce fichier contrôle le processus principal d'Electron, qui exécute une instance de Node.js et est responsable du cycle de vie de votre application, de l’affichage des interfaces natives, de l’exécution des opérations privilégiées, et de la gestion des processus de rendu.

Renderer processes (or renderers for short) are responsible for displaying graphical content. Vous pouvez charger une page web dans un moteur de rendu en le faisant pointer sur une adresse web ou un fichier HTML local. Les renderers se comportent de manière très similaire aux pages Web normales et ont accès aux mêmes API Web.

Dans la section suivante du tutoriel, nous allons apprendre comment enrichir le processus de rendu avec des API à privilèges et comment communiquer entre les processus.