この記事は『リマインダーを開発する』の続きになります。先にそちらをご覧ください。
WEBアプリとして開発したカレンダーをエレクトロンへ移植する
| 2025/09/21
エレクトロンの開発環境を構築しアプリを開発する
- エレクトロンを導入する前に再び問う。なぜアプリ化?
前章でも解説しましたが、WEBブラウザで展開するカレンダーでは、ブラウザを閉じると通知が来ない。
デスクトップへ通知がこないと、気が付かないで予定時刻が過ぎ去ってしまう。
これでは予定表をつくる意味がない。
OSへインストールできるアプリにすると通知でサウンドも鳴らせ、さらにタスクバーへアイコンを表示させ、PC起動と同時にバックグランドで常時予定を監視し時間がきたら通知してくれる。
つまり使えるリマインダーの条件はOSへインストールできることが重要になるのです。
- アプリ化はわかった、ではなぜエレクトロンか?
ElectronはHTML / CSS / JavaScript で作ったWebアプリを、そのまま Windows / Mac / Linux のアプリに変換できる仕組みを持っている。つまり移植が楽にできる。開発コストが非常に少なくすむ。
基盤がNode.js + Chromium(googleクロム)なので、他のアプリと違いHTML、CSS、Javascriptがそのまま使え、WEB技術がそのまま使える。アプリ専用のネイティブ言語を覚える必要がない。
しかもOS専用のインストーラーを作成できる。windows の場合、EXEファイルのインストーラを書き出せる。
余談ですが、VSCode、Slack、Discord もElectronで作られているそうです。
エレクトロンの開発環境を構築する
- 構成内容
ファイル構成は下記の様に構築します。
第一章までで制作したindex.htmlはJS,CSSひとまとめにしてこの段落の下で確認できます。
また、PHPは今後不要なので削除します。
D:\xampp8\htdocs\shv-reminder
├index.html ← カレンダー 兼リマインダー
├main.js ← Electron起動用
└node_modules/ ← npm install electron したら自動でできる
リマインダーを開発する第1章迄で制作したindex.html:https://studio-happyvalley.com/wp/data/index.html.txt
Powershellを使いコマンドでインストール
- まずはNode.js とElectronをインストール
エレクトロンはNode.js上に展開されますので、まずはNode.jsをインストールします。
ここからの作業はコマンド入力がメインになります。
数行で済むので、コマンド入力が苦手な人でもなんとかなるかと思います。
windowsはデフォルトのコマンドプロンプトもいいですが、私はpowershellを使ってます。
さて、インストールする場所はプロジェクト用のフォルダを専用に作り、そこへインストールします。
私は『shv-reminder』としました。
ちなみに私の場合、毎回プロジェクトディレクトリへ行くのが面倒なのでpowerShellのショートカットに作業フォルダの場所を設定して使ってます。

- electronまでインストールする
インストールサイズは5~600MBとかなりでかいですが、まあ、そういうもののようで気にせず進めましょう。(-_-;)
※Node.jsのインストールは割と環境に左右されず、楽にインストールできます。
Electronのインストールまで完了したらルートに node_modulesフォルダ ができます。
mkdir my-calendar
cd my-calendar
npm init -y
npm install electron --save-dev
- Node.js の簡単な説明
Node.js はElectron以外にもいろいろに使われます。Net上のJS関連プロジェクトはほとんどNode.jsでインストールできるものがほとんどです。私の場合は他に、WebPackで使ってます。
さて、Node.js はシステム全体にインストールされる(例:C:\Program Files\nodejs\)のでプロジェクトのフォルダで npm install すると、そのフォルダに node_modules が作られます。
このnode_modulesはプロジェクト別に必ず自動で作られます。名前は同じでも中身はまったく別、今回はelectron専用のモジュールになります。
つまり、Electronは 「そのプロジェクトのフォルダ」 にインストールされるのが普通です
そのためプロジェクトのサイズがかなり膨大になります。
私の場合でいうと600MB以上になりました。
なんとかしたいなと思ってます(-_-;)
必要ファイルの作成
- そのほかの必要ファイルの作成:main.js
これまでの作業でNode.jsとElectronが入りました。
この後はpowershellから離れていくつかJSONファイルなど必要なファイルを作成します。
まずはmain.jsを作成します。
const { app, BrowserWindow, Notification } = require('electron');
function createWindow() {
const win = new BrowserWindow({
width: 1000,
height: 800,
webPreferences: {
nodeIntegration: true
}
});
win.loadFile('index.html'); // ←これまで作ったカレンダーがそのまま使える!
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
- そのほかの必要ファイルの作成:package.json
package.json にスタートスクリプトを追記
今後使うコマンドを設定します。
これで Electronアプリとしてカレンダーが開くようになります。
普通のブラウザじゃなく、アプリのウィンドウとして出てきます。
"scripts": {
"start": "electron ."
}
- package.json にmain.jsを設定
最初の方に'description'というプロパティがあるので
"index.js"を"main.js"と修正。
//package.jsonサンプル↓
"name": "shv-reminder",
"version": "1.0.0",
"description": "Simple Reminder and Calendar",
"main": "main.js",

いよいよアプリとして起動
- powershellで実行
これで、これまでブラウザで表示していたindex.htmlをアプリとして表示させる準備が整いました。
powershellに戻って下記を入力
npm start
- まずはカレンダーをアプリとして表示させました。
ここまででブラウザで表示させていたカレンダーをElectronのアプリとして表示できました。
ただし、現在はデータの保存をPHPで処理する設定のままなので、予定もローカルストレージへ簡易保存している状態です。
今後はそれらをElectronで保存できるようにjavascriptを変更していきます。
また、検証はshift+cntrl+iで開発ツールが開きます。
今後、JSなどはそれを使って開発・検証していきます。
ElectronはサーバーではないのでNode.jsを使ってデータを保存する
javascriptでファイルを保存できる『fs』とは?
- Electron は Node.js がベースになっているので、Node.js の標準ライブラリがそのまま使えます。
その中でもファイル操作を担当するのが fs (File System) モジュールです。
fs は「ファイルを読む/書く」ためのモジュールです。
ブラウザでは localStorage に JSON を保存していましたが、Electron では fs を使って直接 JSON ファイルを読み書きします。
ファイルを読み込む → fs.readFileSync("path", "utf-8")
ファイルに書き込む → fs.writeFileSync("path", data, "utf-8")
つまり、ブラウザの localStorage.setItem/getItem と同じ役割を 実ファイルに対して行うのが fs です。
ただし、このままだとセキュリティエラー
- Electron の「レンダラープロセスでは require がそのまま使えない
Electron はメインプロセス(Node.js が動く部分)レンダラープロセス(ブラウザ環境っぽい部分)の二つで動いています。
現状、fsをブラウザパート(レンダラー)で読み込んでいますが、セキュリティの都合で require が無効になっているので、
このままでは、エラー『Uncaught ReferenceError: require is not defined』 が出てしまいます。
- 解決方法は セキュリティを無視か、preload.js を使う方法か
本来はセキュリティ上の仕様でエラーになりますが、いったん開発用ということでレンダラー側でrequireを有効にする方法があります。
もう一つは、preload.js を作ります。つまりメインプロセス側でデータの保存と読み込みをやってしまおうという方法です。
ただ、この方法はかなり工程を踏み、electron側の構文を使いますので今回は、簡単なレンダラー側でrequireを有効にする方法で進めます。
下記のようにmain.jsを書き換えます。
function createWindow() {
const win = new BrowserWindow({
width: 1000,
height: 800,
webPreferences: {
nodeIntegration: true, // ←これでレンダラで require が使える
contextIsolation: false, // ←合わせて false に
enableRemoteModule: false
}
});
prompt()が使えないため、保存ファイル名を固定で進める
現状コードにどう組み込むか?
- 1. currentUserKey廻りを置き換える
現状jsのトップに let currentUserKey = null;がありますがそのあとにディレクトリ関連のコードを追加します。
仮にユーザーキーを『cal-default'』で進めます。
let currentUserKey = 'cal-default';
const fs = require("fs");
const path = require("path");
const dataDir = path.join(__dirname, "data");
const dataFile = path.join(dataDir, `${currentUserKey}.json`);
- 2. getAllNotes を置き換える
次に読み込み箇所を置き換えます。
localStorage 部分を fs に置き換えるのがポイントです。
async function initUser() {//このブロックは不要になったのですべて削除します。
// 旧: localStorage
function getAllNotes() {
const notes = localStorage.getItem(currentUserKey);
return notes ? JSON.parse(notes) : {};
}
// 新: fs版
// ディレクトリがなければ作る
function ensureDir() {
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
}
function loadNotes() {
ensureDir();
if (!fs.existsSync(dataFile)) return {};
const raw = fs.readFileSync(dataFile, "utf-8");
return JSON.parse(raw || "{}");
}
function saveNotes(notes) {
fs.writeFileSync(dataFile, JSON.stringify(notes, null, 2), "utf-8");
}
let notes = loadNotes();
// --- ノートの操作関数は現状のままでOK ---
function getAllNotes() {
return loadNotes();
}
- 3.saveNoteToStorageを置き換える
function saveNoteToStorage() {のスコープ内すべてを書き換え
// 旧: localStorage + fetch(PHP)
function saveNoteToStorage() {
let notes = getAllNotes();
notes[date] = { title, body };
localStorage.setItem(currentUserKey, JSON.stringify(notes));
fetch("save_notes.php", { ... });
}
これを fs に置き換えると:
// 新: fsのみ
function saveNoteToStorage() {
const title = document.getElementById("noteInputTitle").value.trim();
const body = document.getElementById("noteInputBody").value.trim();
let notes = getAllNotes();
if (date) { // ← ここで date が undefined でないことを確認
if (title || body) {
notes[date] = { title, body };
} else {
delete notes[date];
}
}
saveNotes(notes); // ←これで完了!
closeModal();
renderCalendar();
}
最後に不要になったinitUser();を削除すれば完了です。
これで、data/cal-default.json に日付ごとの予定が保存されるようになります。
試しに予定を記入してjsonファイルを開いてみると、こんな感じの JSON が記録されています。
{
"2025-09-01": {
"title": "会議",
"body": "14:00からオンラインミーティング"
}
}

予定を入れられるデスクトップカレンダー、アプリ版の完成