どうやって動いているの?
Wailsは、webkitフロントエンドを備えた、何の変哲もないGoアプリです。 アプリ全体のうちGoの部分は、アプリのコードと、ウィンドウ制御などの便利な機能を提供するランタイムライブラリで構成されています。 フロントエンドはwebkitウィンドウであり、フロンドエンドアセットをウィンドウ上に表示します。 フロントエンドからも、JavaScriptでランタイムライブラリを呼び出すことができます。 そして最終的に、Goのメソッドはフロントエンドにバインドされ、ローカルのJavaScriptメソッドであるかのように、フロントエンドから呼び出すことができます。

アプリのメインコード
概要
アプリは、wails.Run()メソッドを1回呼び出すことで、構成することができます。 このメソッドで、アプリのウィンドウサイズやウィンドウタイトル、使用アセットなどを指定することができます。 基本的なアプリを作るコードは次のとおりです:
package main
import (
"embed"
"log"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
app := &App{}
err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
OnStartup: app.startup,
OnShutdown: app.shutdown,
Bind: []interface{}{
app,
},
})
if err != nil {
log.Fatal(err)
}
}
type App struct {
ctx context.Context
}
func (b *App) startup(ctx context.Context) {
b.ctx = ctx
}
func (b *App) shutdown(ctx context.Context) {}
func (b *App) Greet(name string) string {
return fmt.Sprintf("Hello %s!", name)
}
オプション
上記のコードでは、次のオプションが指定されています:
Title- ウィンドウのタイトルバーに表示されるテキストWidth&Height- ウィンドウの大きさAssets- アプリのフロントエンドアセットOnStartup- ウィンドウが作成され、フロントエンドの読み込みを開始しようとする時のコールバックOnShutdown- アプリを終了しようとするときのコールバックBind- フロントエンドにバインドさせたい構造体インスタンスのスライス
A full list of application options can be found in the Options Reference.
アセット
Wailsでフロントエンドアセット無しのアプリを作成することはできないため、Assetsオプションは必須オプションです。 アセットには、一般的なWebアプリケーションでよく見かけるような、html、js、css、svg、pngなどのファイルを含めることができます。アセットバンドルを生成する必要は一切なく、そのままのファイルを使用できます。 アプリが起動すると、アセット内のindex.htmlが読み込まれます。この時点で、フロントエンドはブラウザとして動作するようになります。 embed.FSを使ってアセットファイルが格納されたディレクトリを指定しますが、ディレクトリの場所はどこでも構いません。 embedで指定するパスは、frontend/distのように、アプリのメインコードから相対的に見たディレクトリパスになります:
//go:embed all:frontend/dist
var assets embed.FS
起動時に、Wailsはindex.htmlが含まれるディレクトリを再帰的に探します。 他のすべてのアセットは、当該ディレクトリから相対的に読み込まれます。
本番用のバイナリファイルには、embed.FSで指定されたアセットファイルが含まれるため、アプリ配布時に、バイナリファイルとは別にアセットファイルを付加させる必要はありません。
wails devコマンドを使って開発モードでアプリを起動した場合、アセットはディスクから読み込まれ、アセットファイルが更新されると、自動的にアプリがライブリロードされます。 アセットの場所は、embed.FSでの指定値から推定されます。
詳細は、アプリ開発ガイドをご覧ください。
アプリのライフサイクル
フロントエンドがindex.htmlを読み込もうとする直前に、OnStartupで指定されたメソッドがコールバックされます。 Goの標準的なcontextがこのメソッドに渡されます。 このメソッドに引数で渡されるContextは、今後、Wailsのラインタイムを呼び出すときに必要になるため、通常は、このContextへの参照を保持しておいてください。 同様に、アプリがシャットダウンされる直前には、OnShutdownで指定されたコールバックが呼び出され、Contextも渡されます。 index.htmlに含まれるすべてのアセットが読み込み終わったときに呼び出されるOnDomReadyコールバックもあります。これは、JavaScriptのbody onloadイベントと同等のものです。 また、OnBeforeCloseを指定すると、ウィンドウを閉じる(またはアプリを終了する)イベントにフックさせることもできます。
メソッドのバインド
Bindオプションは、Wailsアプリで最も重要なオプションの1つです。 このオプションでは、フロントエンドに公開する、構造体のメソッドを指定することができます。 構造体は、従来のWebアプリにおける"コントローラ"の立ち位置であるとお考えください。 アプリが起動すると、Bindオプションで指定されている構造体を対象に、その中にあるパブリックメソッド(大文字で始まるメソッド名)を探します。そして、フロントエンドのコードからそれらのメソッドを呼び出せるJavaScriptが生成されます。
Wailsで構造体を正しくバインドするためには、構造体のインスタンスをオプションで指定してください。
下記のコードでは、新しくAppインスタンスを作成し、wails.Run関数のBindオプションの中で、そのインスタンスを追加しています:
package main
import (
"embed"
"log"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
app := &App{}
err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Bind: []interface{}{
app,
},
})
if err != nil {
log.Fatal(err)
}
}
type App struct {
ctx context.Context
}
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s!", name)
}
構造体は、好きな数だけバインドできます。 Bindには、構造体のインスタンスを渡すようにしてください:
//...
err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Bind: []interface{}{
app,
&mystruct1{},
&mystruct2{},
},
})
You may bind enums types as well. In that case you should create array that will contain all possible enum values, instrument enum type and bind it to the app via EnumBind:
type Weekday string
const (
Sunday Weekday = "Sunday"
Monday Weekday = "Monday"
Tuesday Weekday = "Tuesday"
Wednesday Weekday = "Wednesday"
Thursday Weekday = "Thursday"
Friday Weekday = "Friday"
Saturday Weekday = "Saturday"
)
var AllWeekdays = []struct {
Value Weekday
TSName string
}{
{Sunday, "SUNDAY"},
{Monday, "MONDAY"},
{Tuesday, "TUESDAY"},
{Wednesday, "WEDNESDAY"},
{Thursday, "THURSDAY"},
{Friday, "FRIDAY"},
{Saturday, "SATURDAY"},
}
//...
err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Bind: []interface{}{
app,
&mystruct1{},
&mystruct2{},
},
EnumBind: []interface{}{
AllWeekdays,
},
})
wails devコマンド(または、wails generate moduleコマンド)を実行すると、以下のものを含むフロントエンドモジュールが生成されます:
- バインドされたすべてのメソッドのJavaScript
- バインドされたすべてのメソッドのTypeScript宣言
- バインドされたメソッドの引数または返り値で使用されているGoの構造体のTypeScript宣言
これにより、強力な型付けがなされたデータ構造を使用して、フロントエンドから簡単にGoのコードを呼び出すことができます。
フロントエンド
概要
フロントエンドは、webkitによってレンダリングされるファイル群です。 ブラウザとWebサーバが一体となったような動きをします。 使用できるフレームワークやライブラリの制限はほぼありません1。 フロントエンドとGoコードとの相互のやり取りについて、主なポイントは次のとおりです:
- バインドされたGoメソッドの呼び出し
- ランタイムメソッドの呼び出し