Skip to content

Instantly share code, notes, and snippets.

@tatsuyasusukida
Last active May 1, 2022 00:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tatsuyasusukida/43f9c962297db5991bc4511f351e0eb0 to your computer and use it in GitHub Desktop.
Save tatsuyasusukida/43f9c962297db5991bc4511f351e0eb0 to your computer and use it in GitHub Desktop.
☎️ Electronでfetchを使ってmainプロセスと通信する方法

☎️ Electronでfetchを使ってmainプロセスと通信する方法

この記事について

この記事ではElectronでrendererプロセスから Fetch API を使ってmainプロセスと通信する方法について紹介します。

おおまかな手順

おおまか手順を下記に示します。

  1. コーディング
  2. 動作確認

コーディング

ターミナルで下記のコマンドを実行してコーディングの準備をします。

mkdir electron-fetch
cd electron-fetch
npm init -y
npm install --save express
npm install --save-dev electron
touch index.html main.js renderer.js

main.js

エディタでmain.jsを開いて下記の内容を入力します。

クリックしてmain.jsへ移動

ポイントを下記に示します。

  1. createWindow関数内でExpressを使ってWebサーバーを作成します。
  2. /へのGETリクエストに対してindex.htmlのファイル内容を返送します。
  3. /render.jsへのGETリクエストに対してrender.jsのファイル内容を返送します。
  4. fetchを使って通信するためのAPIを設けます(このサンプルではGET /api/status)。
  5. router.listenメソッドを呼び出してWebサーバーを起動します。
  6. mainWindow.loadURLメソッドを呼び出してWebサーバーにアクセスします。

index.html

エディタでindex.htmlを開いて下記の内容を入力します。

クリックしてindex.htmlへ移動

renderer.js

エディタでrenderer.jsを開いて下記の内容を入力します。

クリックしてrenderer.jsへ移動

package.json

エディタでpackage.jsonを開いてmainプロパティを下記の通り変更します。

  "main": "main.js",

動作確認

ターミナルで下記のコマンドを実行してコンソールに{"ok":true}と出力されることを確認します。

npx electron .

上記のコマンドを実行してElectronのウィンドウが起動した様子です。コンソールに{"ok":true}と出力されています

おわりに

Electronではプロセス間通信(IPC)のAPIを用意されているのでrendererプロセスからmainプロセスに通信するにあたってわざわざfetchを使う必要はありません。しかしながら、通信モデルがメッセージパッシングなのでリクエスト/レスポンス型の通信の場合はawaitするためにPromiseを書かなければならず、また、複数のリクエストを同時に送信する場合はユニークなIDなどを発行してリクエストを特定できるようにする必要があります。これに対してfetchの場合はawaitするためにPromiseを書かなくてよく、また、リクエストとレスポンスの対応を自動で管理してくれるのでとても楽です。デメリットとしてはTCPポートを一つオープンする必要があり、また、このTCPポートには同じPCからアクセスできてしまうのでセキュリティ上の懸念があります。

IPCとfetchの使い分けですが、同じ目的のために複数の手段があるとコーディングの時に迷いが生じるので私は次のようにルールを決めています。

  • 原則としてfetchを使う
  • メニューなどのElectron固有の機能を使う場合にはIPCを使う
  • mainプロセスからrendererプロセスに通信する場合はIPCを使う

Electronを使ったWindows向けのデスクトップアプリをバージョンアップの時にC♯に移植する仕事をしましたがXAMLなどに苦しめられた苦い思い出があります。どうにかして完成にまで漕ぎ着けましたがバグだらけで使いものにならなかったので結局Electronに戻して満足のいくものを作り直しました。Web技術を活用してデスクトップアプリを開発できるElectronは本当に素晴らしい思います(C♯も言語自体は素晴らしいと思うのですが...)。

この記事をお読みの方の中でWindowsアプリ開発に苦労した経験をお持ちの方やコツなどをご存知の方はお気軽にコメントをいただければ幸いです。最後までお読みいただきありがとうございました!

/node_modules/
/package-lock.json
# Do not ignore package-lock.json other than gist.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"> <!-- <1> -->
<title>Electronでfetchを使ってmainプロセスと通信する方法</title>
</head>
<body>
<h1>Electronでfetchを使ってmainプロセスと通信する方法</h1>
<p>デベロッパーツールを開いてコンソールに{"ok":true}と出力されていることをご確認ください。</p>
<script src="renderer.js"></script> <!-- <2> -->
</body>
</html>
const {app, BrowserWindow} = require('electron')
const path = require('path')
const express = require('express')
function createWindow () {
const router = express() // <1>
router.get('/', (req, res) => { // <2>
res.sendFile(path.join(__dirname, 'index.html'))
})
router.get('/renderer.js', (req, res) => { // <3>
res.sendFile(path.join(__dirname, 'renderer.js'))
})
router.get('/api/status', (req, res) => { // <4>
res.send({ok: true})
})
const port = process.env.PORT || '3000'
router.listen(port, () => { // <5>
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
})
mainWindow.loadURL('http://127.0.0.1:3000/') // <6>
mainWindow.webContents.openDevTools()
})
}
app.whenReady().then(() => {
createWindow()
})
app.on('window-all-closed', function () {
app.quit()
})
{
"name": "electron-fetch",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"dev": "electron ."
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"electron": "^18.2.0"
},
"dependencies": {
"express": "^4.18.1"
}
}
main()
async function main () {
try {
const response = await fetch('/api/status')
const body = await response.json()
const text = JSON.stringify(body)
console.info(text)
} catch (err) {
console.error(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment