Electron実践プロジェクト
実践プロジェクトを通じて、ウィンドウ管理、IPC通信、ファイル操作、データ保存、インターフェース最適化など、Electronのさまざまな機能に対する理解を深めることができます。
プロジェクト1:デスクトップノートアプリ
機能設計
- ノートの作成、編集、削除
- 自動保存とローカル保存
- 複数ウィンドウでの同時編集に対応
- シンプルなインターフェースで、Markdownレンダリングを提供
技術選定
- フロントエンドフレームワーク:Vue または React
- レンダラープロセス:ノート内容を表示し、リッチテキスト / Markdownをサポート
- データ保存:
electron-storeノートデータの保存 - メインプロセス:ウィンドウ、メニュー、ショートカットキーを管理
- IPC通信:レンダラープロセスがメインプロセスにデータの読み取りや保存を要求
実装手順
1、Electronプロジェクトを作成
mkdir electron-notes cd electron-notes npm init -y npm install electron electron-store
2、メインプロセス main.js
サンプル
const { app, BrowserWindow, ipcMain } = require('electron')
const Store = require('electron-store')
const store = new Store()
let mainWindow
app.whenReady().then(() => {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: `${__dirname}/preload.js`
}
})
mainWindow.loadFile('index.html')
})
// IPC:ノートを読み取る
ipcMain.handle('get-notes', () => store.get('notes', []))
// IPC:ノートを保存する
ipcMain.handle('save-notes', (event, notes) => store.set('notes', notes))
const Store = require('electron-store')
const store = new Store()
let mainWindow
app.whenReady().then(() => {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: `${__dirname}/preload.js`
}
})
mainWindow.loadFile('index.html')
})
// IPC:ノートを読み取る
ipcMain.handle('get-notes', () => store.get('notes', []))
// IPC:ノートを保存する
ipcMain.handle('save-notes', (event, notes) => store.set('notes', notes))
3、プリロードスクリプト preload.js
サンプル
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
getNotes: () => ipcRenderer.invoke('get-notes'),
saveNotes: (notes) => ipcRenderer.invoke('save-notes', notes)
})
contextBridge.exposeInMainWorld('api', {
getNotes: () => ipcRenderer.invoke('get-notes'),
saveNotes: (notes) => ipcRenderer.invoke('save-notes', notes)
})
4、レンダラープロセス index.html
サンプル
<!DOCTYPE html>
<html>
<body>
<textarea id="note"></textarea>
<button id="saveBtn">保存</button>
<script>
const textarea = document.getElementById('note')
const btn = document.getElementById('saveBtn')
window.api.getNotes().then(notes => {
textarea.value = notes.join('\n\n')
})
btn.addEventListener('click', async () => {
await window.api.saveNotes([textarea.value])
alert('保存成功')
})
</script>
</body>
</html>
<html>
<body>
<textarea id="note"></textarea>
<button id="saveBtn">保存</button>
<script>
const textarea = document.getElementById('note')
const btn = document.getElementById('saveBtn')
window.api.getNotes().then(notes => {
textarea.value = notes.join('\n\n')
})
btn.addEventListener('click', async () => {
await window.api.saveNotes([textarea.value])
alert('保存成功')
})
</script>
</body>
</html>
コード解説
electron-store:ローカルデータ保存を担当し、JSONを自動シリアライズするipcMain.handle/ipcRenderer.invoke:レンダラープロセスとメインプロセス間の通信を実装contextBridge:APIをレンダラープロセスに安全に公開textarea+ボタン:ノート内容の編集と保存をシンプルに実装
プロジェクト2:ファイルマネージャー
インターフェース設計
- 左側にディレクトリツリー、右側にファイルリスト
- ファイル操作に対応:新規作成、削除、名前変更
- ファイルを目標ディレクトリにドラッグ&ドロップ
- 簡単な検索と並べ替え機能
ファイル操作の実装
- Node.js fs モジュール:ファイルの読み取り、書き込み、削除
- path モジュール:パス処理
- IPC通信:レンダラープロセスがメインプロセスにファイル操作を要求
サンプル
// メインプロセス main.js
const fs = require('fs')
const path = require('path')
ipcMain.handle('read-dir', (event, dirPath) => fs.readdirSync(dirPath))
ipcMain.handle('create-file', (event, filePath) => fs.writeFileSync(filePath, ''))
ipcMain.handle('delete-file', (event, filePath) => fs.unlinkSync(filePath))
const fs = require('fs')
const path = require('path')
ipcMain.handle('read-dir', (event, dirPath) => fs.readdirSync(dirPath))
ipcMain.handle('create-file', (event, filePath) => fs.writeFileSync(filePath, ''))
ipcMain.handle('delete-file', (event, filePath) => fs.unlinkSync(filePath))
サンプル
// レンダラープロセス
window.api.readDir('/Users/username/Documents').then(files => {
console.log(files)
})
window.api.readDir('/Users/username/Documents').then(files => {
console.log(files)
})
ドラッグ&ドロップ機能
- レンダラープロセスがリッスンする
dragstartとdropイベント - メインプロセスが使用する
fs.rename或fs.copyFileファイルを移動する
サンプル
// レンダラープロセスのdropイベント
const dropArea = document.getElementById('fileList')
dropArea.addEventListener('drop', (event) => {
event.preventDefault()
const filePath = event.dataTransfer.files[0].path
window.api.moveFile(filePath, targetDir)
})
const dropArea = document.getElementById('fileList')
dropArea.addEventListener('drop', (event) => {
event.preventDefault()
const filePath = event.dataTransfer.files[0].path
window.api.moveFile(filePath, targetDir)
})
パフォーマンス最適化
- 大規模ディレクトリには非同期読み取りを採用し、レンダラープロセスのブロッキングを回避する
- ファイルリストの仮想化により、DOMレンダリングの負荷を軽減する
- 頻繁な操作にはバッチ更新またはWeb Workerを使用する
まとめ
この2つの実践プロジェクトを通じて、以下を習得できます:
- ウィンドウとマルチプロセス通信:親子ウィンドウ、IPC
- ローカルデータ保存:electron-store、fs
- インターフェースとパフォーマンス最適化:仮想リスト、遅延読み込み、非同期処理
- ネイティブ機能の統合:ファイル操作、ドラッグ&ドロップ、メニュー、ショートカットキー
これらのプロジェクトは、今後の複雑なデスクトップアプリ開発の基礎テンプレートとして使用でき、多機能なElectronアプリを迅速に構築するのに役立ちます。
その他の拡張