Wails Go+webアセットによるデスクトップアプリ開発メモ(bun+vue3利用)
Goで、Webアセット(html/css/js)を使ってデスクトップアプリ開発ができるWailsを触ってみたメモです。
現在私はマルチプラットフォームのデスクトップアプリを、RustによるTauriメインで開発を行っていますが、Rustのビルド時間が長く、アプリケーション開発効率を下げているため、
Wailsも技術候補として検証したいと思います。
ここ最近の利用数の伸びや、今後のRoadmap https://github.com/wailsapp/wails/discussions/1484 などから、
それなりに今後に期待できるプロジェクトと思います(企業スポンサーが付けばより安心ですが)
ちなみにGoでGUI開発はこれまでもwalkというモジュールを使って行ってきましたが、
すでに数年間、commitが止まっていること、
またUIの実装をGoで行うと、リッチな表現力のある言語と比べて厳しいこと、
Web frontに慣れ親しんだ身としては、webviewベースのツールに利点があることから、
別の選択肢を探していました。
参考にさせていただいた記事
windows開発環境メモ
Goが入っている事は前提ですが、省略します。
インストール
go install github.com/wailsapp/wails/v2/cmd/wails@latest
プロジェクトの作成
project_nameディレクトリが作成され、その下にファイル群が自動で生成されます。
※私はvueが最も使い慣れているので、vueを選択します。react,svelte・・その他いくつか選択可能です
wails init -n <project_name> -t vue
project_name:.
| .gitignore
| app.go
| go copy.mod
| go.mod
| go.sum
| main.go
| README.md
| tree.txt
| wails.json
|
+---build
| | appicon.png
| | README.md
| |
| +---darwin
| | Info.dev.plist
| | Info.plist
| |
| \---windows
| | icon.ico
| | info.json
| | wails.exe.manifest
| |
| \---installer
| project.nsi
| wails_tools.nsh
|
\---frontend
| index.html
| package.json
| README.md
| vite.config.js
|
+---dist
| gitkeep
|
+---src
| | App.vue
| | main.js
| | style.css
| |
| +---assets
| | +---fonts
| | | nunito-v16-latin-regular.woff2
| | | OFL.txt
| | |
| | \---images
| | logo-universal.png
| |
| \---components
| HelloWorld.vue
|
\---wailsjs
+---go
| \---main
| App.d.ts
| App.js
|
\---runtime
package.json
runtime.d.ts
runtime.js
開発環境で実行
wails dev
これだけで、稼働します。
さらに快適に開発を行うため、初期の変更を行いました。
viteのバージョンを上げる
frontendのpackage.jsonを見てみると、使われているviteのバージョンが低いので、
変更してみる
"devDependencies": {
"@vitejs/plugin-vue": "^3.0.3",
"vite": "^3.0.7"
}
↓
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"vite": "^5.3.1"
}
frontのビルドは、npmでなく、bunを使う
javascript runtimeは、高速なbunが安定してきたので、
こちらに変更。
"frontend:install": "bun install",
"frontend:build": "bun run build",
"frontend:dev:watcher": "bun run dev",
↓この部分を変更
"frontend:install": "npm install",
"frontend:build": "npm run build",
"frontend:dev:watcher": "npm run dev",
これで、wails dev
を実行しても、きちんと動きました。
ビルド
npm→bunへの変更、viteのバージョンを上げた状態で、
ビルドしてexeの生成をしてみます。
コマンドはこちら。
wails build
warning: GOPATH set to GOROOT (C:\Program Files\Go) has no effect
Done.
INFO Wails is now using the new Go WebView2Loader. If you encounter any issues with it, please report them to https://github.com/wailsapp/wails/issues/2004. You could also use the old legacy loader with `-tags native_webview2loader`, but keep in mind this will be deprecated in the near future.
Built 'C:\Project\project_name\build\bin\project_name.exe' in 12.231s.
♥ If Wails is useful to you or your company, please consider sponsoring the project:
https://github.com/sponsors/leaanthony
こちらも問題なくexeが生成され、実行確認できました。
感想
Tauriに比べて、backend側のコード変更時に反映される時間が、圧倒的に速い。
気軽なツール系のアプリから試してみようと思います。
実装を進めて分かったこと
frontからbackendの関数call
- Appという、コアで動いているstructに、関数を追加します
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s, It's show time2", name)
}
- front側で、関数をimportして、そのまま使えます。
import { Greet } from '../../wailsjs/go/main/App';
function greet() {
Greet("なまえ").then(result => {
alert(result);
})
}
frontからbackendに渡すデータは、stringに限らず、
int, structなどもきちんと処理してくれます。
(javascriptのObjectが、structに対応しています)
逆に、backendからfrontへ返す値も同様に、stringに限らず、structを渡すとObjectに変換して渡してくれます。
例:
func (a *App) Greet(data struct {
StartDate string
EndDate string
Syubetsu int
}) ProdData {
//StartDate: "2021-02-01", EndDate: "2021-03-31", Syubetsu:3 を受け取ります
fmt.Printf("%+v", data);
return ProdData{
StartDate: "2024-01-01",
EndDate: "2024-01-31",
Syubetsu: 1,
}
}
import { Greet } from '../../wailsjs/go/main/App';
Greet({
StartDate: "2021-02-01",
EndDate: "2021-03-31",
Syubetsu: 3,
}).then(result => {
//Objectとして、こちらが返ってきます。
//{
// StartDate: "2024-01-01",
// EndDate: "2024-01-31",
// Syubetsu: 1,
//}
console.log('result:', result);
})
ただし、返り値を2つ以上にした場合、最初の返り値しか受け取れませんでした。
例:
func (a *App) Greet() (string, string) {
return "返り値1", "返り値2"
}
import { Greet } from '../../wailsjs/go/main/App';
Greet().then(result => {
console.log('result:', result);
//返り値2の方は取得できません。
//log: result: 返り値1
})
このような場合は、少し強引ですが、以下のようにすることで複数の値を返すことはできました。
func (a *App) Greet() []interface{} {
return []interface{"返り値1", "返り値2"}
}
import { Greet } from '../../wailsjs/go/main/App';
Greet().then(([result1, result2]) => {
console.log('result1:', result1, 'result2:', result2);
//log: result1: 返り値1 result2: 返り値2
})
wails + bun の生産性ヤバい
以前作ったwalkのアプリをwailsで再構築し、機能改善を行っていますが、
wails + bun + vue の生産性、やばいです。
普段frontの開発をされている方なら、基本的に普段使用のフレームワーク(reactでもsvelteでも)はそのまま使えると思いますので、
webアプリ開発と、ほぼ同じ感覚でデスクトップアプリが作れます。
小ネタ
起動時のオプションで少しだけ起動時間を短縮
wails dev
時のgo mod tidyをスキップするオプション -m
開発環境での動作はwails devで行いますが、
この際、毎回go mod tidyが自動的に実行されます。
モジュールの変更を行っていないのであれば、余計なビルド時間が発生するので、
この処理を普段はスキップすることで効率的に開発がすすめられます。
(default)
PS C:\Project\sample> wails dev
Wails CLI v2.9.2
Executing: go mod tidy (初回は、しばらく固まる)
Generating bindings:
...
(skip go mod tidy)
PS C:\Project\sample>wails dev -m
Wails CLI v2.9.2
Generating bindings:
...
devオプション一覧:
起動時のwindowの各種オプション
windowのサイズや背景色など、アプリケーションの設定は
go内での起動時に設定可能。
詳しくはこちらで
例:
err := wails.Run(&options.App{
Title: "Menus Demo",
Width: 800,
Height: 600,
DisableResize: false,
Fullscreen: false,
WindowStartState: options.Maximised,
Frameless: true,
MinWidth: 400,
MinHeight: 400,
MaxWidth: 1280,
MaxHeight: 1024,
StartHidden: false,
HideWindowOnClose: false,
BackgroundColour: &options.RGBA{R: 0, G: 0, B: 0, A: 255},
AlwaysOnTop: false,
AssetServer: &assetserver.Options{
Assets: assets,
Handler: assetsHandler,
Middleware: assetsMidldeware,
},
Menu: app.applicationMenu(),
Logger: nil,
LogLevel: logger.DEBUG,
LogLevelProduction: logger.ERROR,
OnStartup: app.startup,
OnDomReady: app.domready,
OnShutdown: app.shutdown,
OnBeforeClose: app.beforeClose,
CSSDragProperty: "--wails-draggable",
CSSDragValue: "drag",
EnableDefaultContextMenu: false,
EnableFraudulentWebsiteDetection: false,
Bind: []interface{}{
app,
},
EnumBind: []interface{}{
AllWeekdays,
},
ErrorFormatter: func(err error) any { return err.Error() },
SingleInstanceLock: &options.SingleInstanceLock{
UniqueId: "c9c8fd93-6758-4144-87d1-34bdb0a8bd60",
OnSecondInstanceLaunch: app.onSecondInstanceLaunch,
},
DragAndDrop: &options.DragAndDrop{
EnableFileDrop: false,
DisableWebViewDrop: false,
CSSDropProperty: "--wails-drop-target",
CSSDropValue: "drop",
},
Windows: &windows.Options{
WebviewIsTransparent: false,
WindowIsTranslucent: false,
BackdropType: windows.Mica,
DisablePinchZoom: false,
DisableWindowIcon: false,
DisableFramelessWindowDecorations: false,
WebviewUserDataPath: "",
WebviewBrowserPath: "",
Theme: windows.SystemDefault,
CustomTheme: &windows.ThemeSettings{
DarkModeTitleBar: windows.RGB(20, 20, 20),
DarkModeTitleText: windows.RGB(200, 200, 200),
DarkModeBorder: windows.RGB(20, 0, 20),
LightModeTitleBar: windows.RGB(200, 200, 200),
LightModeTitleText: windows.RGB(20, 20, 20),
LightModeBorder: windows.RGB(200, 200, 200),
},
// ZoomFactor is the zoom factor for the WebView2. This is the option matching the Edge user activated zoom in or out.
ZoomFactor: float64,
// IsZoomControlEnabled enables the zoom factor to be changed by the user.
IsZoomControlEnabled: bool,
// User messages that can be customised
Messages: *windows.Messages
// OnSuspend is called when Windows enters low power mode
OnSuspend: func()
// OnResume is called when Windows resumes from low power mode
OnResume: func(),
// Disable GPU hardware acceleration for the webview
WebviewGpuDisabled: false,
},
Mac: &mac.Options{
TitleBar: &mac.TitleBar{
TitlebarAppearsTransparent: true,
HideTitle: false,
HideTitleBar: false,
FullSizeContent: false,
UseToolbar: false,
HideToolbarSeparator: true,
OnFileOpen: app.onFileOpen,
OnUrlOpen: app.onUrlOpen,
},
Appearance: mac.NSAppearanceNameDarkAqua,
WebviewIsTransparent: true,
WindowIsTranslucent: false,
About: &mac.AboutInfo{
Title: "My Application",
Message: "© 2021 Me",
Icon: icon,
},
},
Linux: &linux.Options{
Icon: icon,
WindowIsTranslucent: false,
WebviewGpuPolicy: linux.WebviewGpuPolicyAlways,
ProgramName: "wails"
},
Debug: options.Debug{
OpenInspectorOnStartup: false,
},
})
windowsでのビルド高速化
winで起動(wails dev
)が遅いとの議論がされてます。
私も実際windowsでは初回の起動に時間がかかっているのですが、
原因が分からずにいました。
上記のgo mod tidy
をskipする -m 以外のところでいうと、
windows defenderのCPU利用率が毎回上がっていることが観測されていました。
同じことを指摘しているコメントを発見。
Windows Defender > Exclusions > Add an exclusion > Process > C:\Program Files\Go\bin\go.exe
And while I'm at it I added VSCode and Node.
Note : It's important to add the exclusion as a Process and not a File so it excludes any child process that Go spawns, adding it as a file/folder exclusion didn't give the same results.
解決
windows defenderの利用をやめ、ESET Internet Security(有料)に切り替えたところ、
goらしい超速ビルドになりました。
wails製アプリ本番リリースしました
経理業務のためのツールを開発し、
クライアントに、nsisによるビルドで、windowsインストーラとして展開していますが、
問題なく稼働してます。
リリースまでにハマるポイントは少なかったように思います。
参考までに、ビルドサイズはインストーラ8Mb, exe16Mbでした。