プロセス間通信
Electron で機能豊かなデスクトップアプリケーションを構築するには、プロセス間通信 (IPC) が重要な要素です。 なぜなら、Electron のプロセスモデルではメインプロセスとレンダラープロセスが異なる責務を担っており、UI からネイティブ API を呼び出したり、ネイティブメニューからウェブコンテンツの変更をトリガーしたりといった多くの共同タスクの実行には、IPC が唯一の方法となるからです。
IPC チャンネル
Electron では、 ipcMain
と ipcRenderer
モジュールで開発者が定義した「チャンネル」を介してメッセージを渡すことによって、プロセスが通信します。 これらのチャンネルは 任意 (好きな名称を指定可能) かつ 双方向的 (両方のモジュールで同じチャンネル名を使用可能)です。
このガイドでは、アプリのコードの参考になる基本的な IPC のパターンを具体的な例で説明します。
コンテキスト分離されたプロセスを理解する
実装の詳細に進む前に、コンテキスト分離されたレンダラープロセスにて プリロードスクリプト を使って Node.js と Electron モジュールをインポートするアイデアを知っておきましょう。
- Electron のプロセスモデルの概要については、プロセスモデルのドキュメント をご一読ください。
contextBridge
モジュールでプリロードスクリプトから API を公開する方法の手ほどきについては、コンテキスト分離のチュートリアル をご確認ください。
パターン 1: レンダラーからメインへ (片方向)
レンダラープロセスからメインプロセスへ片方向の IPC メッセージを送信するには、ipcRenderer.send
API を使用してメッセージを送信し、それを ipcMain.on
APIで受信します。
通常このパターンは、ウェブコンテンツからメインプロセスの API を呼び出すために使用します。 ここでは、プログラムによってウインドウのタイトルを変更できる簡単なアプリを作成することで、このパターンを実証しようと思います。
このデモでは、メインプロセス、レンダラープロセス、プリロードスクリプトにコードを追加する必要があります。 コード全体は以下のとおりですが、以降の節で各ファイルを個別に説明します。
- main.js
- preload.js
- index.html
- renderer.js
const { app, BrowserWindow, ipcMain } = require('electron/main')
const path = require('node:path')
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
ipcMain.on('set-title', (event, title) => {
const webContents = event.sender
const win = BrowserWindow.fromWebContents(webContents)
win.setTitle(title)
})
mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('electronAPI', {
setTitle: (title) => ipcRenderer.send('set-title', title)
})
<!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'">
<title>Hello World!</title>
</head>
<body>
Title: <input id="title"/>
<button id="btn" type="button">Set</button>
<script src="./renderer.js"></script>
</body>
</html>
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
const title = titleInput.value
window.electronAPI.setTitle(title)
})