Electron - Build cross platform desktop apps

Commençons par présenter Electron…

Electron est un framework JavaScript développé et soutenu par GitHub afin d’offrir aux utilisateurs une expérience desktop de leur application. À l’instar du couple Ionic & Cordova qui propose de construire des applications mobiles multi-plateformes, Electron propose de développer des applications desktop sur Windows (>= Windows 7) , MacOS (>= OS X 10.8) et Linux (Ubuntu 12.04 ou supérieur, Fedora 21 et Debian 8).

Exemples d’applications

Des applications comme les IDE Atom et Visual Studio Code ou encore les applications de messageries Slack et Rocket Chat sont construites à partir d’Electron. Une liste plus exhaustive des applications développées avec ce framework est disponible ici.

Comment est construite une application Electron?

Les équipes de GitHub se sont basées sur le constat que la plupart des développeurs avaient de bonnes connaissances dans les technologies Web (HTML, JS, CSS), grâce à l’engouement pour des frameworks JavaScript comme Angular, VueJS, etc.

Electron permet aux développeurs de réaliser des applications desktop rapidement en se basant sur:

  • Chromium pour réaliser le rendu des pages (GUI)
  • NodeJS pour le côté back-end.

L’API JavaScript proposée par le framework permet d’enrichir l’application avec les possibilités que les systèmes d’exploitation nous proposent comme l’accès au filesystem, l’utilisation des notifications, etc. Electron propose une API qui se découpe en trois modules.

Module Main Process

Dans Electron, le processus principal est nommé “main process”, il est créé au lancement de l’application en exécutant le fichier main.js défini dans le fichier package.json .

{ "name": "HelloIpponApp", "main": "main.js", "scripts": { "start": "electron main.js" }, "devDependencies": { "electron": "^1.3.5" } }

Le “main process” se présente comme un script Node JS classique qui va permettre de créer et contrôler des pages web en créant des instances d’objets BrowserWindows. On peut voir BrowserWindows comme une fenêtre du navigateur Chromium dans laquelle va s’exécuter un processus de rendu. Il y aura donc un processus de rendu créé pour chaque BrowserWindows (fenêtre) actif dans l’application.

Source https://github.com/electron/

Module Renderer Process

Le “renderer process” se présente comme une page web classique, mais qui a la particularité de pouvoir utiliser des modules NodeJS directement depuis la balise script.

<!DOCTYPE html><html><body><div>...</div><script>const remote = require('electron').remote; console.log(remote.app.getVersion()); </script></body></html>

Exemple d’utilisation de module (ci-dessus remote) dans une page de renderer process.

Module Both Process

Le “both processes” propose des modules transversaux utilisables à la fois dans le main et le renderer process. Pour citer quelques modules transverses on trouve clipBoard pour gérer les copier/coller, les variables d’environnement ou encore crashReporter pour faire remonter les anomalies que l’application a rencontrées.

Les technologies c’est bien, mais encore ?

Electron, facilite également la génération des applications sur des systèmes hétérogènes et permet de les packager afin de simplifier leur déploiement. Electron, va encore plus loin, car le framework propose un mécanisme d’update afin de maintenir les applications à jour sans impacter l’expérience utilisateur (mise à jour silencieuse). Nous reviendrons en détail sur cette fonctionnalité dans la dernière partie de cet article.

Voyons quelques objets que nous propose le main process…

L’objet App

L’objet App permet de contrôler le cycle de vie de l’application.

const {app} = require('electron') app.on('ready', function(){ /** … */} ); app.on('window-all-closed', () => { app.quit(); })

On retrouve les événements émis tels que :

  1. ready: événement déclenché quand l’initialisation de l’application est finie,
  2. window-all-closed: événement déclenché quand toutes les fenêtres sont fermées,
  3. before-quit: événement déclenché avant de quitter l’application,
  4. activate : événement déclenché quand l’application est active.

Les browser modules

Le BrowserWindows correspond à la fenêtre du navigateur Chromium dans laquelle va s’exécuter un processus de rendu. Les instances de BrowserWindows disposent d’une API afin de permettre aux fenêtres de charger des pages HTML, mais également d’effectuer certaines actions en fonction d’événements liés aux fenêtres (exemple : enter-html-full-screen, ready-to-show , hide,…).
Un BrowserWindows se définit de cette façon :

const {BrowserWindow} = require('electron') /**…*/ let window=new BrowserWindow({width: 800, height: 600}) window.loadURL('file://'+ __dirname +'/index.html'); window.once('ready-to-show', () => { window.webContents.send('play-sound', path.join(__dirname,/sound/tintin.mp3'))});

Les menus

Comme dans toute application desktop, il y a possibilité de réaliser un menu :

La réalisation des menus dans Electron se fait via la description d’objets JSON :

const {app, Menu} = require('electron') /** … /const template = [{ label: 'Edit', submenu: [ { label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' }, { label: 'Paste', accelerator: 'CmdOrCtrl+V', role: 'paste' }, ] }] /* … */const menu = Menu.buildFromTemplate(template) Menu.setApplicationMenu(menu)

Les trays

Les trays correspondent aux icônes présentes dans la barre des tâches des systèmes d’exploitation. Ils permettent un accès rapide à l’application en ouvrant un menu contextuel par simple clic sur l’icône. Voici un exemple avec le trays Docker.

La déclaration d’un tray avec Electron se fait de la façon suivante :

const {app, Menu, Tray} = require('electron') /**...*/ tray =new Tray('/path/to/my/icon'); const contextMenu = Menu.buildFromTemplate([ {label: 'Bouton 1', click: function(){ callFunction1();}}, {label: 'Bouton 2', click: function(){ callFunction2(); }} ]); tray.setToolTip('My application.'); tray.setContextMenu(contextMenu);

Et le cross plateforme dans tout cela ?

La livraison des applications sur différents supports est une problématique que les éditeurs de logiciels connaissent bien.
Electron facilite grandement le portage des applications sur différentes plateformes avec l’aide de “jobs” définis dans le fichier package.json. Une fois les tâches de build définies dans le fichier json, les modules Node d’Electron se chargent de lire les configurations du fichier de description et de builder le code source en fonction des paramètres prescrits.

Comment builder les sources pour mon OS?

Le build de l’application pour les différentes plateformes est géré par le module electron-packager et les tâches Node qui lui sont associées.
Exemple minimaliste de configuration des tâches de portage dans le fichier package.json :

{ "name": "HelloIpponApp", "main": "main.js", "scripts": { "start": "electron main.js", "pack-linux": "electron-packager . HelloIpponApp --asar --overwrite --platform=linux --arch=x64 --out=out", "pack-win": "electron-packager . HelloIpponApp --asar --overwrite --platform=win32 --arch=ia32 --icon=src/assets/icons/icon.icns --out=out", "pack-mac": "electron-packager . HelloIpponApp --asar --overwrite --platform=darwin --arch=x64 --icon=src/assets/icons/app.icns --out=out" }, "author": "Ippon Tech", "devDependencies": { "electron": "^1.3.5", "electron-packager": "^8.5.2" } }

Les tâches d’electron-packager se présentent sous la forme suivante :

electron-packager <sourcedir><appname>--platform=<platform>--arch=<arch> [optional flags...]

  1. sourcedir : emplacement des sources
  2. appname : nom de l’application
  3. plateform : windows (win32); macOS (darwin), linux (linux)
  4. arch : type d’architecture (x64/ia32/…)
  5. out : répertoire où le résultat du build sera placé

D’autres arguments peuvent être ajoutés en options de la ligne de commande afin de spécifier comment le build doit être réalisé.
Une fois les tâches electron-packager définies, il suffit de les exécuter en utilisant des outils comme npm ou yarn ; le résultat sera ensuite placé dans le répertoire fixé par le flag –out.

$ yarn pack-linux $ yarn pack-mac $ yarn pack-win

À noter qu’il est nécessaire d’installer Wine pour packager des applications pour les plateformes Windows depuis des machines Unix/Linux.

Mais à quoi sert l’option –asar ?

L’option “–asar” permet de générer des fichiers d’archive au format .asar. Ces fichiers sont des fichiers d’archives spécifiques à Electron (similaires aux tar ou jar), ils peuvent faire office d’artefact afin d’archiver vos différentes versions de projet.

Installation (avec yarn ou npm) et archivage avec l’utilitaire asar :

$ yarn add asar $ asar pack HelloIpponApp.asar

Ces fichiers d’archives peuvent également être utilisés directement par Electron afin d’accéder à des ressources qui sont déjà compactées.

const {BrowserWindow} = require('electron') let win =new BrowserWindow({width: 400, height: 300}) win.loadURL('file:///path/to/ippon.asar/static/index.html')

Les sources, c’est bien, mais où sont les setups ?

Une fois les tâches du module electron-packager effectuées, il reste une étape importante avant de diffuser l’application : générer les setups afin de déployer et d’installer proprement les applications sur les systèmes d’exploitation.

Sous Windows

Afin de générer les setups d’installation Windows, la documentation officielle recommande les modules electron-winstaller et electron-squirrel-startup.

Le module electron-wininstaller requiert l’ajout de script de configuration qui va permettre de générer les setups du type MSI (paramétrage du nom de l’application, emplacement du logo, etc…).

Il est également nécessaire de rajouter un handler dans le code (main.js) pour Squirrel afin de lui permettre l’installation, les mises à jour silencieuses et la désinstallation de l’application.

Sous MacOS

Pour le déploiement sur MacOS, on peut également se baser sur Squirrel Mac. Il existe également d’autres outils plus simples de pris en main comme Appdmg.

Cependant, il faudra rajouter un mécanisme de signature, si l’application doit être poussée sur des stores comme celui d’Apple (MAS).

Pour conclure

Electron est fait pour vous si vous souhaitez réaliser de nouvelles applications desktop en partant de zéro, ou bien dans l’objectif d’adapter votre application Web Angular/Ionic existante afin d’ouvrir de nouvelles perspectives.

Une autre force de ce framework réside dans son support et sa communauté GitHub qui est beaucoup plus active que celle de son principal concurrent NW.js. Enfin ce framework se distingue aussi techniquement sur plusieurs points.

Ressources