Wails の仕組み
Wails のアーキテクチャと、ネイティブパフォーマンスを実現する仕組みを理解する
Wails は、バックエンドに Go、フロントエンドに Web 技術を使用してデスクトップアプリケーションを構築するためのフレームワークです。ただし Electron とは異なり、ブラウザーを同梱せず、オペレーティングシステムのネイティブ WebViewを使用します。
Electron との主な違い:
| 項目 | Wails | Electron |
|---|---|---|
| ブラウザー | OS が提供する WebView | 同梱の Chromium(約 100MB) |
| バックエンド | Go(コンパイル済み) | Node.js(インタープリター実行) |
| 通信 | インメモリブリッジ | IPC(プロセス間通信) |
| バンドルサイズ | 約 15MB | 約 150MB |
| メモリ | 約 10MB | 約 100MB 以上 |
| 起動時間 | <0.5s | 2-3s |
コアコンポーネント
1. ネイティブ WebView
Wails は、オペレーティングシステムに組み込まれた Web レンダリングエンジンを使用します。
WebView2(Microsoft Edge WebView2)
- Chromium ベース(Edge ブラウザーと同じ)
- Windows 10/11 にプリインストール済み
- Windows Update による自動更新
- 最新の Web 標準を完全にサポート
WebKit(Safari のレンダリングエンジン)
- macOS に組み込み済み
- Safari ブラウザーと同じエンジン
- 優れたパフォーマンスとバッテリー駆動時間
- 最新の Web 標準を完全にサポート
WebKitGTK(WebKit の GTK 移植版)
- パッケージマネージャーでインストール
- GNOME Web(Epiphany)と同じエンジン
- Web 標準を十分にサポート
- 軽量で高性能
これが重要な理由:
- ブラウザーを同梱しない → アプリのサイズを縮小
- OS ネイティブ → 統合性とパフォーマンスが向上
- 自動更新 → OS の更新によってセキュリティパッチを適用
- 使い慣れたレンダリング → システムブラウザーと同じ
2. Wails ブリッジ
ブリッジは Wails の中核であり、Go と JavaScript の間の直接通信を可能にします。
仕組み:
- フロントエンドが Go メソッドを呼び出す(自動生成されたバインディング経由)
- ブリッジが呼び出しを JSON にエンコードする(メソッド名と引数)
- ルーターが登録済みサービス内から Go メソッドを見つける
- Go メソッドが実行され、値を返す
- ブリッジが結果をデコードし、フロントエンドに返す
- JavaScript で結果を受け取って Promise が解決される
パフォーマンス特性:
- インメモリ:ネットワークのオーバーヘッドも HTTP もなし
- 可能な場合はゼロコピー(大容量データ向け)
- デフォルトで非同期:どちら側もブロックしない
- 型安全:TypeScript の型定義を自動生成
3. サービスシステム
フロントエンドに Go の機能を公開するには、サービスの使用を推奨します。
// Define a service (just a regular Go struct)
type GreetService struct {
prefix string
}
// Methods with exported names are automatically available
func (g *GreetService) Greet(name string) string {
return g.prefix + name + "!"
}
func (g *GreetService) GetTime() time.Time {
return time.Now()
}
// Register the service
app := application.New(application.Options{
Services: []application.Service{
application.NewService(&GreetService{prefix: "Hello, "}),
},
})サービスの検出:
- Wails は起動時に構造体をスキャンします
- エクスポートされたメソッドをフロントエンドから呼び出せるようになります
- TypeScript バインディング用に型情報が抽出されます
- エラー処理は自動です(Go のエラー → JS の例外)
生成された TypeScript バインディング:
// Auto-generated in frontend/bindings/GreetService.ts
export function Greet(name: string): Promise<string>
export function GetTime(): Promise<Date>サービスを使用する理由:
- 型安全:TypeScriptを完全にサポート
- 自動検出:メソッドを手動で登録する必要はありません
- 体系的な整理:関連する機能をグループ化
- テスト可能:サービスは単なるGo構造体です
4. イベントシステム
イベントにより、コンポーネント間でパブリッシュ/サブスクライブ通信を行えます。
ユースケース:
- ウィンドウ間通信:あるウィンドウから別のウィンドウへ通知
- バックグラウンドタスク:GoサービスからUIへ進捗を通知
- 状態の同期:複数のウィンドウを同期した状態に維持
- 疎結合:コンポーネント間の直接参照が不要
例:
// Go: Emit an event
app.Event.Emit("user-logged-in", user)// JavaScript: Listen for event
import { Events } from '@wailsio/runtime'
Events.On('user-logged-in', (user) => {
console.log('User logged in:', user)
})アプリケーションのライフサイクル
ライフサイクルを理解すると、リソースを初期化するタイミングとクリーンアップするタイミングが分かります。
ライフサイクルフック:
app := application.New(application.Options{
Name: "My App",
// Cleanly intercept quit requests (e.g. unsaved changes).
ShouldQuit: func() bool { return true },
// Called when the app is confirmed to be quitting — save state, close connections, etc.
OnShutdown: func() {},
})application.OptionsにはOnStartupフィールドはありません。起動時の処理は、サービスのServiceStartup(ctx, options)、app.Event.OnApplicationEvent(events.Common.ApplicationStarted, ...)で登録したコールバック、または単にapp.Run()より前に配置します。
ビルドプロセス
Wailsがアプリケーションをビルドする仕組みを見てみましょう。
ビルド手順:
- Goコードを解析
- サービスを走査してエクスポート済みメソッドを検出
- パラメーターと戻り値の型を抽出
- メソッドシグネチャを生成
- TypeScriptバインディングを生成
- サービスごとに
.tsファイルを作成 - 完全な型定義を含める
- JSDocコメントを追加
- フロントエンドをビルド
- バンドラー(Vite、webpackなど)を実行
- ミニファイして最適化
frontend/dist/に出力
- Goをコンパイル
- 最適化を有効にしてコンパイル(
-ldflags="-s -w") - ビルドメタデータを含める
- プラットフォーム固有のコンパイルを実行
- アセットを埋め込む
- フロントエンドファイルをGoバイナリに埋め込む
- アセットを圧縮
- 単一の実行可能ファイルを作成
結果:すべてが埋め込まれた単一のネイティブ実行可能ファイル。
開発環境と本番環境
Wailsの動作は、開発環境と本番環境で異なります。
特性:
- ホットリロード:フロントエンドの変更を即座に再読み込み
- ソースマップ:元のソースコードを使用してデバッグ
- DevTools:ブラウザーのDevToolsを使用可能
- ログ出力:詳細なログ出力を有効化
- 外部フロントエンド:開発サーバー(Vite)から配信
仕組み:
利点:
- 変更結果を即座に確認
- すべてのデバッグ機能を利用可能
- 反復作業を高速化
特性:
- 埋め込みアセット:フロントエンドをバイナリに組み込み
- 最適化済み:ミニファイおよび圧縮済み
- DevToolsなし:デフォルトで無効
- 最小限のログ出力:エラーのみ
- 単一ファイル:すべてを1つの実行可能ファイルに格納
仕組み:
利点:
- 単一ファイルで配布
- サイズを縮小(ミニファイ済み)
- パフォーマンスの向上
- 外部依存関係なし
メモリモデル
メモリ使用量を理解すると、効率的なアプリケーションを構築できます。
メモリ領域:
- Goヒープ
- サービスとアプリケーションの状態
- Goのガベージコレクターによって管理
- 単純なアプリでは通常5-10MB
- WebViewのメモリ
- DOM、JavaScriptヒープ、CSS
- WebViewのエンジンによって管理
- 単純なアプリでは通常10-20MB
- ブリッジのメモリ
- 通信用のメッセージバッファ
- 最小限のオーバーヘッド(<1MB)
- 可能な場合は大容量データをゼロコピーで処理
最適化のヒント:
- 大容量データの転送を避ける:IDを渡し、詳細は必要に応じて取得します
- 更新にはイベントを使用する:フロントエンドからポーリングしないでください
- 大きなファイルをストリーミングする:ファイル全体をメモリに読み込まないでください
- リスナーをクリーンアップする:不要になったイベントリスナーは削除してください
セキュリティモデル
Wailsは、デフォルトで安全なアーキテクチャを提供します:
セキュリティ機能:
- メソッドの許可リスト
- 呼び出せるのはエクスポートされたメソッドのみ
- 非公開メソッドにはアクセス不可
- サービスの明示的な登録が必要
- 型の検証
- 引数をGoの型と照合
- 無効な型を拒否
- インジェクション攻撃を防止
- eval()を使用しない
- フロントエンドから任意のGoコードを実行することは不可
- 呼び出せるのは事前定義されたメソッドのみ
- 動的なコード実行なし
- コンテキストの分離
- 各ウィンドウが独自のコンテキストを保持
- サービスで呼び出し元のコンテキストを確認可能
- ウィンドウごとの権限設定が可能
ベストプラクティス:
- Goでユーザー入力を検証する(フロントエンドを信頼しないでください)
- 認証と認可にはコンテキストを使用する
- ファイル操作の前にファイルパスをサニタイズする
- 高コストな処理をレート制限する
次のステップ
アプリケーションのライフサイクル - 起動、終了、ライフサイクルフックについて理解します 詳しく見る →
Goとフロントエンド間のブリッジ - ブリッジの仕組みを詳しく掘り下げます 詳しく見る →
ビルドシステム - Wailsがアプリケーションをビルドする仕組みを理解します 詳しく見る →
構築を始める - ここまでに学んだことをチュートリアルで実践します チュートリアル →