◾️ はじめに
現在、Electron + Vue + TypeScript を使ってアプリケーションを開発しています。
その中で、Electronの子プロセスで、Expressサーバーを起動する実装に苦戦したため、同じ問題で悩んでいる方の助けになればと思い、この記事を書きました。
参考になれば幸いです。
※今回は Electronの概要についての説明は省略します。
📂 ディレクトリ構造
electron
|- server
| |- server.ts
|- main.ts
◾️ やりたかったこと
Electronのメインプロセスとは別に子プロセスを作成し、サーバーを起動したい!
◾️ 問題点
最初に、Node.jsのchild_process.spawn()
を使用して子プロセスを起動しようとしました。
しかし、spawn()
を使用した際、サーバーが正常に起動しないという問題が発生しました。
❌ spawn()
を使った場合の問題点
child_process.spawn()
を使用すると、Electronのメインプロセスとは完全に独立したプロセスとしてサーバーが起動してしまうため、以下の問題が発生しました。
1.サーバーが起動しない
spawn()
でserver.tsを実行しようとすると、サーバーが正常に動作しませんでした。
stdio: 'inherit'
を設定してもログが出力されず、原因の特定が難しかったです。
stdio: "inheritとは
子プロセスの標準入出力(stdin, stdout, stderr)を親プロセスと共有する。
→ 子プロセスのログやエラーメッセージがそのまま親プロセスのコンソールに出力される。
2.メインプロセスと通信できない
spawn()
で起動したサーバープロセスは完全に独立したプロセスになるため、Electronのメインプロセスと IPC 通信ができず、状態管理が難しくなりました。
3.Electronの終了時にサーバープロセスを制御しづらい
detached: true
を設定すると、Electronを終了してもサーバープロセスが生き続ける可能性があります。
detached: trueとは
親プロセス(Electron)と子プロセス(サーバー)の紐付けを解除し、完全に独立したプロセスとして動作させることができる。
◾️ 解決策
child_process.fork()
を使用することで、期待通りの動作を実現できました✨
child_process.fork()
は、Node.jsのモジュールとして別プロセスを起動するため、spawn()
とは異なり、Electronのメインプロセスと同じNode.js実行環境を引き継ぎます。
その結果、server.ts
の依存関係や環境の違いによる問題が発生せず、fork()
を使用することで正常にサーバーを起動できました。
一方で、spawn()
を使用すると、完全に独立したプロセスとして起動されるため、Electronの実行環境を継承できず、サーバーが正しく動作しなかったのだと考えられます。
◾️ 実装方法
Express を使って、シンプルなローカルサーバーを作成します。
import express from 'express'
import log from 'electron-log'
const PORT = 9090
const server = express()
// サーバー起動
server.listen(PORT, () => {
log.info(`Local server started at http://localhost:${PORT}`)
})
import { app, BrowserWindow } from 'electron'
import * as path from 'path'
import { fork } from 'child_process'
let mainWindow: BrowserWindow | null
let serverProcess: any
// アプリケーションが準備完了になった時に実行
app.on('ready', () => {
// サーバースクリプトのパスを取得
const serverPath = path.join(__dirname, 'server/server.js')
// 子プロセスとしてサーバーを起動
const serverProcess = fork(serverPath, [])
// メインウィンドウの作成
mainWindow = new BrowserWindow({
'width': 800,
'height': 600,
'show': true
})
})
// すべてのウィンドウが閉じられたときに実行
app.on('window-all-closed', () => {
// サーバープロセスが存在していたら終了させる
if (serverProcess) {
serverProcess.kill()
}
// アプリケーションを終了
app.quit()
})
◾️ 最後に
最後まで読んでいただき、ありがとうございます!同じように悩んでいる方の助けになれば嬉しいです😊