ยท hands on

Anatomy of an Electron 4 application

In Electron 4 applications, there are two main processes: the main process and the renderer process. The main process is responsible for displaying the GUI, managing BrowserWindow instances and registering global shortcuts.

Overview of main concepts in Electron 4 applications.

Contents

main process

Every Electron app has one (and just one!) main process. The main process is used to display a GUI and started from the main script defined package.json.

Concerns

  • Entry point for Electron applications
  • Creates and manages BrowserWindow instances
  • Registers global shortcuts
  • Creates native menus
  • Shows native GUI
  • Responds to auto-update events

renderer process

Each web page in Electron runs in a separate renderer process. If not limited, web pages running in a renderer process have to power to access Node.js modules.

Concerns

  • Takes care of showing your HTML & JS in the Chromium browser
  • Runs UI in webContents instances
  • Access information about audio and video devices using desktopCapturer
  • Can access main process modules via remote module

Characteristics

  • process.type is "renderer"

Example

const { desktopCapturer, ipcRenderer, webFrame } = require('electron');
const { app } = require('electron').remote;
 
webFrame.setZoomFactor(1.0);
webFrame.setVisualZoomLevelLimits(1, 1);
 
console.log('App configuration directory', app.getPath('userData'));

Inter-process communication (IPC)

Using inter-process communication a renderer process can exchange messages with a main process:

renderer.js
import {ipcRenderer} from 'electron';
 
const updateBtn = document.getElementById('updateBtn')
 
updateBtn.addEventListener('click', () => {
  ipcRenderer.send('my-app-event', document.getElementById('notifyVal').value);
});
main.ts
import {BrowserWindow, ipcMain, IpcMessageEvent} from 'electron';
 
const main = new BrowserWindow();
 
ipcMain.on('my-app-event', (event: IpcMessageEvent, price: number) => {
  main.webContents.send('target-price', price);
});

Note: ipcRenderer does not send messages to itself, it sends them to ipcMain. If you want to access the messages within a renderer process, you need to check ipcMain using electron.remote:

renderer.js
import {remote} from 'electron';
 
// ...
 
remote.ipcMain.on('my-app-event', (event, price) => {
  console.log(`Received "${price}" in renderer process.`);
});

Webview Preload Script

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <script>
    require('./renderer.js');
  </script>
  <body>
    <webview
      preload="./preload.js"
      src="https://benny.work"
    ></webview>
  </body>
</html>

You can assign a preload script programmatically:

import fileUrl = require('file-url');
 
const main = new BrowserWindow();
 
const contents = main.webContents;
 
contents.on('will-attach-webview', (event, webPreferences, params) => {
  webPreferences.preloadURL = fileUrl('./preload.js');
});
preload.js
const {ipcRenderer} = require('electron');
 
window.addEventListener('DOMContentLoaded', () => {
  // From guest (webview content) to host (main process)
  window.addEventListener(z.event.WebApp.LIFECYCLE.RESTART, (event) => {
    ipcRenderer.send(EVENT_TYPE.WRAPPER.RELAUNCH);
  });
 
  // From host (main process) to guest (webview content)
  ipcRenderer.on(EVENT_TYPE.WRAPPER.RELAUNCHED, () => {
    window.dispatchEvent(new CustomEvent(EVENT_TYPE.ACTION.CREATE_ACCOUNT));
  });
});
main.ts
import {BrowserWindow} from 'electron';
 
const main = new BrowserWindow();
main.loadFile('index.html');
ipcMain.on(
  EVENT_TYPE.WRAPPER.RELAUNCH,
  async (event: IpcMessageEvent) => {
    console.log('Do some work and send a reply...');
    main.webContents.send(EVENT_TYPE.WRAPPER.RELAUNCHED);
  }
);
Back to Blog