I've migrated my Electron app from Webpack
to Vite
. This migration solved the problems that both electron-webpack
and electron-webpack-quick-start
caused. The next is to upgrade the version of Electron
. Because the boilerplate electron-webpack-quick-start
limits the version of Electron to 8.2.0, I can upgrade Electron
from 8.2.0 to 24.0.0.
A challenge: contextIsolation
defaults to true
#
To upgrade Electron
significantly, I have to take breaking changes into consideration. Electron
12.0 requires the hardest breaking changes: Default Changed: contextIsolation
defaults to true
. This is one of the biggest breaking changes in Electron. contextIsolation: true
is important for security. But, most of the old Electron apps don't support this breaking change.
The reason is that contextIsolation: true
requires a lot of modifications. contextIsolation: true
also forces us to set nodeIntegration: false
. This means Node.js
modules don't work in the renderer process. And contextIsolation: true
disables the @electron/remote
module which allows the renderer process to access Electron
APIs in the main process.
Even Atom
, which was born as a killer app of Electron
, didn't support contextIsolation: true
. Now Pulsar takes over the source code of Atom
, but does not yet support contextIsolation: true
.
So, it is a big challenge.
Understanding Inter-process communication of Electron #
After reading the official document of Electron, there were four key points to understand inter-process communication.
1. Relationships among the processes #
Electron has three types of process. Main, preload, and renderer. Inter-process communication means the main process communicates with the renderer process through the preload process. The main is separate from the renderer. The preload is attached to the main as a script.
2. Global objects are isolated from the renderer process #
Context isolation is a powerful feature for security. It exposes only the APIs in the preload script. Even global objects like Array.prototype.push
or JSON.parse
are isolated from the renderer process and cannot be modified from the renderer process.
In practice, that means that global objects like
Array.prototype.push
orJSON.parse
cannot be modified by scripts running in the renderer process.
3. Two-way IPC needs async/await #
Two-way IPC needs async/await
. IPC spends a bit more time than direct access. Without async/await
, the original call just gets a Promise
.
const btn = document.getElementById('btn')
const filePathElement = document.getElementById('filePath')
btn.addEventListener('click', async () => {
const filePath = await window.electronAPI.openFile()
filePathElement.innerText = filePath
})
4. contextBridge exposes Node APIs #
Node APIs are not exceptions in context isolation. The contextBridge
can give our renderer access to Node APIs.
const { contextBridge } = require('electron')
const crypto = require('crypto')
contextBridge.exposeInMainWorld('nodeCrypto', {
sha256sum (data) {
const hash = crypto.createHash('sha256')
hash.update(data)
return hash.digest('hex')
}
})
Implementing Inter-process communication of Electron #
I used the vite-electron-builder
boilerplate when migrating my Electron app. Because the boilerplate already supported Electron
24.0.0, I added and modified the code for IPC together with debugging. Two issues I got stuck on happened below.
- ERROR: The symbol "contextBridge" has already been declared
- Latency of IPC
ERROR: The symbol "contextBridge" has already been declared #
After adding the code for IPC, I got the error message 'The symbol "contextBridge" has already been declared.'
[vite:esbuild-transpile] Transform failed with 1 error:
index.cjs:13:7: ERROR: The symbol "contextBridge" has already been declared
The symbol "contextBridge" has already been declared
11 | getCode: (path) => ipcRenderer.invoke('get-code', path)
12 | });
13 | const {contextBridge} = require('electron');
| ^
14 |
The cause was the module unplugin-auto-expose
which was imported in vite.config.js
of vite-electron-builder
boilerplate
. The module was a kind of DSL for IPC and required contextBridge
.
Plugins for automatic
exposeInMainWorld
. Easily export your exposed api frompreload
torenderer
.
I removed this module to solve the conflict.
Latency of IPC #
The latency happened by adding the code for IPC. The direct access to Node API from the renderer process was faster than IPC. The access to Electron
APIs through @electron/remote
was also faster than IPC. So I had to change the flow including IPC because of the latency. Additionally, I also reduced the API calls via IPC.
Finally, implementing IPC was done. Both contextIsolation: true
and nodeIntegration: false
didn't cause any errors. Read it easy
now supports Electron
24.0.0!