Déc 01, 2017 Electron

Electron : l’application de démarrage

Dans cet article nous allons analyser le code de l’application de démarrage que nous avons installée dans le précédent article. On va ainsi voir comment est constituée une application Electron.

On a cette architecture :

package.json

Puisqu’on utilise npm on a un fichier de définition package.json :

Avec ce code :

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "repository": "https://github.com/electron/electron-quick-start",
  "keywords": [
    "Electron",
    "quick",
    "start",
    "tutorial",
    "demo"
  ],
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "~1.7.8"
  }
}

On peut personnaliser les renseignements :

{
  "name": "mon-electron",
  "version": "1.0.0",
  "description": "Mes essais avec Electron",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "keywords": [
    "Electron"
  ],
  "author": "Moi",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "~1.7.8"
  }
}

J’ai :

  • renseigné le nom, l’auteur et la description
  • gardé juste un mot clé
  • supprimé la référence au dépôt Github parce qu’on ne va pas en créer
  • gardé la licence

Un valeur importante est celle de main qui ici est égale à main.js. C’est l’emplacement du processus principal (main process) de l’application.

Dans les dépendances on trouve seulement electron et on va s’en contenter pour le moment.

main.js

Comme je l’ai dit ci-dessus on trouve dans ce fichier Javascript les code pour le processus principal. C’est le point d’entrée de l’application. On y trouve ce code :

const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

const path = require('path')
const url = require('url')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600})

  // and load the index.html of the app.
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

On va détailler tout ça…

On commence par déclarer le mode d’Electron pour avoir accès à toutes ses API :

const electron = require('electron')

On déclare ensuite une constante app pour contrôler le cycle de vie de l’application :

const app = electron.app

On déclare une seconde constante BrowserWindow pour le processus de rendu (en fait pour une instance de Chromium) :

const BrowserWindow = electron.BrowserWindow

On déclare ensuite une variable globale pour conserver la fenêtre active pour des raisons de gestion de la mémoire :

let mainWindow

Ensuite on a une fonction pour la création de la fenêtre :

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600})

  // and load the index.html of the app.
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

Dans le détail on crée une instance de BrowserWindow en précisant les dimensions de la fenêtre :

mainWindow = new BrowserWindow({width: 800, height: 600})

On charge cette fenêtre en précisant l’emplacement du fichier qui contient le code, ici index.html, qui est donc le point d’entrée du processus de rendu :

mainWindow.loadURL(url.format({
  pathname: path.join(__dirname, 'index.html'),
  protocol: 'file:',
  slashes: true
}))

On met enfin en place une écoute de l’événement de fermeture de fenêtre pour principalement supprimer l’instance :

mainWindow.on('closed', function () {
  mainWindow = null
})

Ensuite quand Electron a tout chargé il crée la fenêtre :

app.on('ready', createWindow)

Ensuite on met en place une écoute pour savoir si toutes les fenêtres (parce qu’évidemment on pourrait en avoir plusieurs) sont fermées. On a une particularité pour OS X ou une application peut être active sans fenêtre :

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

On a darwin pour OS X et win32 pour Windows (même en 64 bits)

On a finalement une dernière écoute spécifique à OS X pour récréer une fenêtre s’il n’y en avait plus si on active l’application :

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

Les outils de développement

Notez aussi qu’on a ce code commenté :

// mainWindow.webContents.openDevTools()

Par défaut les outils de développement ne sont pas activés. Pour les avoir il suffit donc de dé-commenter cette ligne avec ce résultat :

Si vous regardez le code que vous pouvez déboguer vous ne trouverez évidemment que celui du processus de rendu parce que le code du main process n’est pas exécuté dans cette fenêtre et est donc inaccessible. Alors comment faire ?

On a besoin d’un débogueur externe qui supporte le protocole de l’inspecteur V8, par exemple VS-Code. Notez que Visual Studio Code a été créé avec Electron. Vous devez donc commencer par l’installer en vous rendant sur cette page.

Ensuite il faut ajouter dans le projet un fichier .vscode/launch.json avec ce code :

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Main Process",
      "type": "node",
      "request": "launch",
      "cwd": "${workspaceRoot}",
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
      "windows": {
        "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
      },
      "args" : ["."]
    }
  ]
}

Ouvrez le dossier de l’application avec VS :

Mettez un point d’arrêt dans main.js :

Tapez sur F5 pour lancer le débogage :

Et normalement ça doit fonctionner !

Vous avez maintenant tout ce qu’il faut pour déboguer votre application ! Il existe d’autres possibilités mais je vous ai présenté là celle qui me semble la plus simple.

Le processus de rendu

Il ne nous reste plus qu’à voir le processus de rendu. On a vu que ce processus commence avec le fichier index.html :

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    <!-- All of the Node.js APIs are available in this renderer process. -->
    We are using Node.js <script>document.write(process.versions.node)</script>,
    Chromium <script>document.write(process.versions.chrome)</script>,
    and Electron <script>document.write(process.versions.electron)</script>.

    <script>
      // You can also require other files to run in this process
      require('./renderer.js')
    </script>
  </body>
</html>

On a une page Html classique mais on trouve ce commentaire :

<!-- All of the Node.js APIs are available in this renderer process. -->

On nous dit que toutes les API d’Electron sont disponibles ici. Par exemple ici on récupère la version de Node :

process.versions.node

On voit également qu’on appelle un fichier Javascript renderer.js :

require('./renderer.js')

C’est là qu’on ajoutera le code pour le rendu, pour le moment ce fichier ne contient aucun code.

Dans le prochain article on verra comment peaufiner notre fenêtre à notre goût.

Laisser un commentaire