はじめに
Electronでデスクトップアプリを作ろうとしたとき、ビルド設定でつまずいた経験はありませんか。メインプロセス、プリロードスクリプト、レンダラープロセスという3つの環境それぞれにwebpackやtscの設定を書き、ホットリロードを効かせようとして深い沼にはまる……。Electron開発の「本題」に入る前に力尽きてしまう、というのはよくある話です。
そんな面倒な設定を肩代わりしてくれるのが、今回紹介する electron-vite です。高速なビルドツールとして人気のViteをベースにしており、Electron向けの最適化があらかじめ組み込まれています。この記事では、electron-viteの特徴から実際のプロジェクト構築までを、手を動かしながら理解できるように解説していきます。
electron-viteとは
electron-viteは、Viteをベースに構築された次世代のElectron向けビルドツールです。Electronアプリケーション特有の「複数プロセス構成」を意識した設計になっており、面倒な設定なしにモダンな開発環境を手に入れられます。
Electronアプリは、大きく分けて以下の3つの環境で動作します。
- メインプロセス - Node.js環境で動作し、ウィンドウ管理やOSとの連携を担当
- プリロードスクリプト - メインとレンダラーの間を安全に橋渡しするセキュアなブリッジ
- レンダラープロセス - Chromiumのブラウザ環境で動作し、UIを表示
electron-viteは、この3つをそれぞれ適切な設定でビルドしてくれます。1つの設定ファイルで完結するため、開発者はアプリのロジックに集中できます。
主な特徴
- Viteの恩恵をそのまま享受 - 高速なコールドスタートと即時のモジュール更新を、Electron開発でもそのまま利用できます
- 事前設定済み - Electron向けに最適化されたデフォルト設定が組み込まれており、ゼロコンフィグでも動作します
- 高速なHMRとホットリロード - レンダラープロセスはHMR、メインプロセスとプリロードはホットリロードに対応し、コード変更が即座に反映されます
- 幅広いフレームワーク対応 - TypeScript、Vue、React、Svelte、SolidJSなどをすぐに使えます
- マルチスレッド対応 - Worker Threads、Child Process、Utility Processをシンプルなimport構文で扱えます
- ソースコード保護 - コードをV8バイトコードにコンパイルし、ソースコードを保護できます
インストール
既存のプロジェクトに追加する場合は、開発依存としてインストールします。
# npm
npm install electron-vite -D
# yarn
yarn add electron-vite -D
# pnpm
pnpm add electron-vite -D
ゼロからプロジェクトを始める場合は、公式のスキャフォールディングツールを使うのが一番手軽です。対話形式でフレームワークを選べます。
# 新規プロジェクトを作成
npm create @quick-start/electron@latest
# フレームワークを指定する場合(例: React + TypeScript)
npm create @quick-start/electron@latest my-app -- --template react-ts
vue、react、svelte、solid、vanilla などのテンプレートが用意されており、それぞれJavaScript版とTypeScript版を選択できます。
基本的な使い方
electron-viteの中心となるのは、プロジェクトルートに置く設定ファイル electron.vite.config.ts です。前述の3プロセスに対応する3つのセクションを持つのが基本形です。
// electron.vite.config.ts
import { defineConfig } from 'electron-vite'
export default defineConfig({
main: {
// メインプロセス用のVite設定
},
preload: {
// プリロードスクリプト用のVite設定
},
renderer: {
// レンダラープロセス用のVite設定
}
})
各セクションには、通常のViteと同じ設定オプションをそのまま書けます。例えばReactを使う場合は、renderer にプラグインを追加します。
// electron.vite.config.ts
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
main: {
// node_modulesの依存をバンドルから除外し、ビルドを高速化
plugins: [externalizeDepsPlugin()]
},
preload: {
plugins: [externalizeDepsPlugin()]
},
renderer: {
plugins: [react()]
}
})
次に、package.json にスクリプトを追加します。ビルド成果物の出力先(デフォルトは out ディレクトリ)を main フィールドで指定する点に注意してください。
{
"name": "electron-app",
"version": "1.0.0",
"main": "./out/main/index.js",
"scripts": {
"dev": "electron-vite dev",
"build": "electron-vite build",
"start": "electron-vite preview"
}
}
これで準備完了です。開発サーバーを起動してみましょう。
npm run dev
コマンド一つでElectronウィンドウが立ち上がり、ソースコードを編集するとレンダラーは即座にHMRで更新され、メインプロセスを変更すればアプリが自動で再起動します。
メインプロセスのエントリーポイントは、通常のElectronアプリと同じように書けます。
// src/main/index.ts
import { app, BrowserWindow } from 'electron'
import { join } from 'path'
function createWindow(): void {
const mainWindow = new BrowserWindow({
width: 900,
height: 670,
webPreferences: {
// プリロードスクリプトを指定
preload: join(__dirname, '../preload/index.js'),
sandbox: false
}
})
// 開発時はViteのdevサーバー、本番時はビルド済みHTMLを読み込む
if (process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
ELECTRON_RENDERER_URL はelectron-viteが開発時に自動でセットしてくれる環境変数です。これにより、開発モードと本番モードで読み込み先を自然に切り替えられます。
実践的なユースケース
セキュアなプリロードでメインとレンダラーを橋渡しする
Electronのセキュリティにおいて重要なのが、contextBridge を使った安全なAPI公開です。electron-viteのプロジェクト構成なら、プリロードスクリプトもTypeScriptでそのまま書けます。
// src/preload/index.ts
import { contextBridge, ipcRenderer } from 'electron'
// レンダラーに公開する安全なAPIを定義
const api = {
// アプリのバージョンを取得する(読み取り専用の安全な操作)
getAppVersion: (): Promise<string> => ipcRenderer.invoke('app:getVersion'),
// 通知イベントを購読する
onUpdateAvailable: (callback: () => void) => {
ipcRenderer.on('update:available', callback)
}
}
// contextBridge経由で安全に公開
contextBridge.exposeInMainWorld('electronAPI', api)
メインプロセス側では、対応するハンドラーを登録します。
// src/main/index.ts(一部抜粋)
import { app, ipcMain } from 'electron'
// レンダラーからの要求に応じてバージョンを返す
ipcMain.handle('app:getVersion', () => {
return app.getVersion()
})
レンダラー側からは、window.electronAPI として型安全に呼び出せます。
// src/renderer/src/App.tsx(一部抜粋)
async function showVersion() {
const version = await window.electronAPI.getAppVersion()
console.log(`現在のバージョン: ${version}`)
}
このように、レンダラーは直接Node.jsのAPIに触れず、プリロードで定義した限られた窓口だけを使います。electron-viteはこのベストプラクティスに沿った構成をデフォルトで用意してくれます。
環境変数を安全に扱う
Viteの流儀に沿って、VITE_ プレフィックスの付いた環境変数はレンダラーに公開され、MAIN_VITE_ などのプレフィックスでプロセスごとに変数を分けて管理できます。.env ファイルに定義するだけで利用できます。
# .env
MAIN_VITE_API_ENDPOINT=https://example.com/api
VITE_APP_TITLE=My Electron App
// レンダラー側で参照
console.log(import.meta.env.VITE_APP_TITLE)
機密情報を含む変数はメインプロセス専用のプレフィックスにしておくことで、うっかりレンダラー(ブラウザ環境)に漏れる事故を防げます。
ソースコードを保護する
商用アプリなどでソースコードを保護したい場合は、V8バイトコードへのコンパイル機能を利用できます。設定にプラグインを追加するだけで、配布物からソースコードを読み取りにくくできます。
// electron.vite.config.ts
import { defineConfig, bytecodePlugin } from 'electron-vite'
export default defineConfig({
main: {
// メインプロセスのコードをバイトコード化
plugins: [bytecodePlugin()]
},
preload: {},
renderer: {}
})
まとめ
electron-viteは、Electron開発における「設定の面倒くささ」を解消してくれる強力なツールです。この記事で紹介したポイントを振り返っておきましょう。
- Viteベースで高速なHMR・ホットリロードを実現し、開発体験を大きく改善する
- メイン・プリロード・レンダラーの3プロセスを1つの設定ファイルで管理できる
- TypeScriptや主要フレームワークにゼロコンフィグで対応している
contextBridgeを使ったセキュアな構成がデフォルトで用意されている- バイトコード化によるソースコード保護にも対応している
まずは npm create @quick-start/electron@latest で生成されるテンプレートを触ってみるのが、electron-viteの快適さを体感する一番の近道です。モダンな開発体験でデスクトップアプリ作りを始めてみてはいかがでしょうか。