はじめに
みなさんこんにちは。インプルの岩崎です。
最近ではAI使ったり、裏側のシステムをつくったりしていましたが、今日はアプリを作ってみるお話です。
Electronとはなに?
今回はElectronというフレームワークを使ってみようと思います。
Electron(エレクトロン)は JavaScript / HTML / CSS を使って デスクトップアプリケーション を開発できるフレームワークです。
利用技術として、次の2つがあります。
- Chromium(ブラウザエンジン)
- Node.js(サーバーサイドJavaScriptランタイム)
この2つを組み合わせることで、ウェブ技術で書かれたアプリを クロスプラットフォームのデスクトップアプリ として配布可能になります。
最小構成で起動させる
早速使っていきましょう!
インストール
まずは下記のコマンドでインストールをします。インストールには、Node.jsが必要です!
# 新しいプロジェクトを作成
mkdir my-electron-app
cd my-electron-app
# npm 初期化
npm init -y
# Electron をインストール
npm install electron --save-dev
最小構成ファイル
インストールが終わったら、下記のような構成にしましょう。これがとりあえずアプリを起動できる最小構成です。最小構成で必要な3つのファイルについての詳細は、この後説明します。
my-electron-app/
├── node_modules/ ← npm install で自動生成(編集しない)
├── package.json
├── package-lock.json
├── main.js ← 自分で作る(Mainプロセス)
└── index.html ← 自分で作る(Rendererプロセス)
package.json
1つ目に package.json
ファイルです。Electronでは通常のNode.jsアプリと同じく package.json
がアプリ全体の設定を担います。インストールする時のオプションによって、ファイルの中の構成は若干変わってしまいますが、とりあえず下記の要素が詰まっていればOKです。
### package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"main": "main.js", # アプリのエントリーポイント
"scripts": {
"start": "electron ." # "npm start" だけでアプリを立ち上げられるようにする
}
}
main.js
2つ目に main.js
です。
Electronアプリはブラウザのように見えて、裏では Mainプロセス が全体を管理しており、司令塔的な役割です。ここでウィンドウを作ったり、index.htmlを読み込んだりします。
### main.js
const { app, BrowserWindow } = require('electron')
function createWindow() {
// ウィンドウを作成
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true // Node.js API を使えるようにする
}
})
// index.html をロード
win.loadFile('index.html')
}
// Electron が起動したらウィンドウ生成
app.whenReady().then(createWindow)
// macOS用の終了処理
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
index.html
3つ目に index.html
(Rendererプロセス)です。
普通のHTMLファイルと一緒の構成ですが、nodeIntegrationを有効にすることで、Node.js APIが使用できるようになっています。ここではwebページを表示し、ユーザーとやり取りをするUI担当をしています。
### index.html(Rendererプロセス)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello Electron</title>
</head>
<body>
<h1>Hello Electron!</h1>
<p>これはElectronで作った最初のアプリです 🎉</p>
</body>
</html>
Electronを起動させてみよう
先ほどまでの設定をすることで、とりあえずアプリを起動させることができます。早速下記のコマンドを実施してみてみましょう。
npm start

すると、画面のポップアップとともにhtmlで入力した内容が無事に表示されました。無事に動作できているようです。
アレンジを加える
それでは、上記ファイルを利用してそれぞれ練習していきましょう。
ファイルは新たに作ってもいいですし、index.htmlに追記していく形でも構いません。お好きなやり方で進めていってください。新たにファイルを作る場合、 win.loadFile('index.html')
の部分でファイルを名前を変えましょう!
テキスト入力とボタン選択
まずは、基本的な部分を習得していきましょう。テキスト入力とボタンの選択処理です。Renderer内だけで処理ができるようにしていきます。先ほどのhtmlファイル内に、下記の処理を追加します。
<h2>1. テキスト入力の練習</h2>
<input type="text" id="txt_ls_textbox" placeholder="ここにテキストを入力">
<button id="submit_textbox">送信</button>
<p id="result_textbox"></p>
<h2>2. ボタン入力の練習</h2>
<button id="btn_ls_sea">海派</button>
<button id="btn_ls_mountain">山派</button>
<p id="result_btn"></p>
<script>
// 1. テキスト入力の練習
const txt_ls_textbox = document.getElementById("txt_ls_textbox"); // テキスト入力の要素を取得
const submit_textbox = document.getElementById("submit_textbox"); // 送信ボタンの要素を取得
const result_textbox = document.getElementById("result_textbox"); // 結果の要素を取得
submit_textbox.addEventListener("click", () => {
const input_text = txt_ls_textbox.value; // 入力されたテキストを取得
result_textbox.textContent = `入力されたテキストは${input_text}です`; // <p>に文字を表示
});
// 2. ボタン入力の練習
const btn_ls_sea = document.getElementById("btn_ls_sea"); // 海派ボタンの要素を取得
const btn_ls_mountain = document.getElementById("btn_ls_mountain"); // 山派ボタンの要素を取得
const result_btn = document.getElementById("result_btn"); // 結果の要素を取得
btn_ls_sea.addEventListener("click", () => {
result_btn.textContent = "あなたは海派ですね!";
});
btn_ls_mountain.addEventListener("click", () => {
result_btn.textContent = "あなたは山派ですね!";
});
</script>
これを実行することにより、テキストの入力とボタンに応じた反応をそれぞれ作ることができます。
テキスト入力に関しては、入力されたテキストを取得し、送信ボタンを押した時に “入力されたテキストは${input_text}です ” で表示させる処理をしています。
ボタン入力に関しては、2つのボタンを用意し、クリックされたボタンごとに “あなたは海派ですね!” と “あなたは山派ですね!” でそれぞれ処理させています。


実行させると、上記の画像のような形でそれぞれ処理されます。問題なく動作していますね!
IPC通信にさせる
続けてIPC通信をさせていきましょう。いままではhtmlファイルだけで良かったのですが、これをすることにより、「Electronらしさ」が出てきます!
まず、基礎的な部分を押さえます。Electronでは Renderer(index.html)↔ Main(main.js) がやり取りするために ipcRenderer
と ipcMain
を使います。また、今回はお試しではありますが、セキュリティ面を考慮した contextIsolation: true + preload.js
の形にもしていきます。
- Renderer(index.html内のJS) →
ipcRenderer.send("チャンネル名", データ)
- Main(main.js) →
ipcMain.on("チャンネル名", (event, データ) => { … })
- Mainから返すとき →
event.reply("別のチャンネル名", 結果)
- Rendererで受け取る →
ipcRenderer.on("別のチャンネル名", (event, 結果) => { … })
main.js
には、IPC通信をしていくために、いくつかの記述を追加していきます。
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
function createWindow() {
// ウィンドウを作成
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
})
// index.html をロード
win.loadFile('index.html')
}
// Electron が起動したらウィンドウ生成
app.whenReady().then(createWindow)
// Rendererから "greet" を受け取る
ipcMain.on("greet", (event, text) => {
const message = `入力された文字は、${text}です! Mainで作成されました。`
event.reply("greet-reply", message)
})
// macOS用の終了処理
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
preload.js
は、新たにファイルを新規作成します。
これを追加することで、ElectronAPIを安全に呼び出せるようになります。
const { contextBridge, ipcRenderer } = require('electron')
// セキュアなAPIをレンダラープロセスに公開
contextBridge.exposeInMainWorld('electronAPI', {
sendGreet: (text) => ipcRenderer.send('greet', text),
onGreetReply: (callback) => ipcRenderer.on('greet-reply', (event, message) => callback(message))
})
index.html
には、<script> の中身を変えていきます。
<h2>テキスト入力の練習 - IPC通信</h2>
<input type="text" id="txt_ls_textbox" placeholder="ここにテキストを入力">
<button id="submit_textbox">送信</button>
<p id="result_textbox"></p>
<script>
// 1. テキスト入力の練習
const txt_ls_textbox = document.getElementById("txt_ls_textbox"); // テキスト入力の要素を取得
const submit_textbox = document.getElementById("submit_textbox"); // 送信ボタンの要素を取得
const result_textbox = document.getElementById("result_textbox"); // 結果の要素を取得
// IPC通信 - ボタンを押したらMainに送信
submit_textbox.addEventListener("click", () => {
const text = txt_ls_textbox.value;
window.electronAPI.sendGreet(text);
});
// IPC通信 - Mainからの応答を受け取る
window.electronAPI.onGreetReply((message) => {
result_textbox.textContent = message;
});
</script>
これらのプログラムを下記のような流れで処理をさせていきます。これで セキュアなIPC通信 が実現できます。
- Renderer(index.html)が
window.electronAPI.greet(name)
を呼ぶ - preload.js 経由で
ipcRenderer.send("greet", name)
が実行される - Main(main.js)が
ipcMain.on("greet", …)
で受け取る - メッセージを作って
event.reply("greet-response", …)
で返す - Rendererで
window.electronAPI.onGreetResponse()
が呼ばれてUIに反映


すると、上記の画像のように通信がうまくいった処理が表示されました。
そのほかの拡張性
先ほどまでやっていたことを広げていくことで、より実用性の高いアプリに仕上げることもできます。
Electronはhtmlで画面の表示を作っているので、cssを使ってUIを充実させることもできます。また、そのほかのweb技術であるTailwind CSSやBootstrap、Materializeも使用することができます。
また、OS連携をしてファイルを開いたり選んだりすることもできますし、ネットワークやAPI連携を使ってAPIキーを使用したアプリを作ることもできます。データの保存や読み書きをすることもできるので、入力関係も問題なしです!
ドキュメントにもさまざまなできることがまとめられていますので、ぜひご覧ください。
おわりに
いかがだったでしょうか?
今回作成したコードは、GitHubにもありますので、よろしければぜひご覧ください。
ご覧いただきありがとうございました!