Quick Start
Mulai dengan Wails v3.
Wails v3 adalah rewrite lengkap dengan peningkatan signifikan dalam arsitektur, performa, dan developer experience. Panduan ini membantu Anda memigrasikan aplikasi v2 ke v3.
Perubahan utama:
Waktu migrasi: 1-4 jam untuk aplikasi tipikal
Di v2, setup aplikasi, konfigurasi window, dan eksekusi digabung dalam satu panggilan wails.Run(). Pendekatan monolitik ini menyulitkan pembuatan banyak window, penanganan error di tahap berbeda, atau pengujian komponen individual aplikasi.
v3 memisahkan concern ini ke fase berbeda: pembuatan aplikasi, pembuatan window, dan eksekusi. Pemisahan ini memberi kontrol eksplisit atas setiap tahap lifecycle aplikasi dan membuat kode lebih modular serta dapat diuji.
v2:
err := wails.Run(&options.App{ Title: "My App", Width: 1024, Height: 768, Bind: []interface{}{ &GreetService{}, },})v3:
app := application.New(application.Options{ Name: "My App", Services: []application.Service{ application.NewService(&GreetService{}), },})
window := app.Window.NewWithOptions(application.WebviewWindowOptions{ Title: "My App", Width: 1024, Height: 768,})
app.Run()Mengapa ini lebih baik:
Di v2, setiap struct yang di-bind memerlukan field context dan method startup(ctx) untuk menerima runtime context. Ini menciptakan coupling ketat antara business logic dan runtime Wails, sehingga kode lebih sulit diuji dan dipahami.
v3 memperkenalkan pola service, di mana struct Anda sepenuhnya standalone dan tidak perlu menyimpan runtime context. Jika service memerlukan akses ke instance aplikasi, service secara eksplisit menerimanya melalui dependency injection alih-alih threading context implisit.
v2:
type App struct { ctx context.Context}
func (a *App) startup(ctx context.Context) { a.ctx = ctx}
func (a *App) Greet(name string) string { return "Hello " + name}v3:
type GreetService struct{}
func (g *GreetService) Greet(name string) string { return "Hello " + name}
// Daftarkan sebagai serviceapp := application.New(application.Options{ Services: []application.Service{ application.NewService(&GreetService{}), },})Mengapa ini lebih baik:
AppServiceStartup() saat Anda perlu inisialisasi, sehingga eksplisitDi v2, semua operasi runtime memerlukan meneruskan context ke fungsi global dari paket runtime. Ini menciptakan coupling ketat ke objek context di seluruh codebase dan membuat API terasa prosedural alih-alih object-oriented.
v3 menggantikan runtime berbasis context dengan pemanggilan method langsung pada objek aplikasi dan window. Operasi dipanggil langsung pada objek yang terpengaruh, sehingga kode lebih intuitif dan object-oriented.
v2:
import "github.com/wailsapp/wails/v2/pkg/runtime"
runtime.WindowSetTitle(a.ctx, "New Title")runtime.EventsEmit(a.ctx, "event-name", data)v3:
// Simpan referensi apptype MyService struct { app *application.App}
func (s *MyService) UpdateTitle() { window := s.app.Window.Current() window.SetTitle("New Title")}
func (s *MyService) EmitEvent() { s.app.Event.Emit("event-name", data)}Mengapa ini lebih baik:
window.SetTitle() lebih jelas daripada runtime.WindowSetTitle(ctx, ...)Di v2, bindings diorganisir berdasarkan paket Go dan nama struct, biasanya menghasilkan path seperti wailsjs/go/main/App. Struktur ini tidak mencerminkan pengelompokan logis dan menyulitkan pencarian fungsionalitas terkait.
v3 mengorganisir bindings berdasarkan nama service dan modul aplikasi, menciptakan struktur logis yang lebih jelas. Bindings di-generate ke direktori bindings yang diorganisir berdasarkan nama aplikasi dan nama service, sehingga lebih mudah memahami fungsionalitas yang tersedia.
v2:
import { Greet } from '../wailsjs/go/main/App'
const result = await Greet("World")v3:
import { Greet } from './bindings/changeme/greetservice'
const result = await Greet("World")Mengapa ini lebih baik:
../wailsjs/go — cukup ./bindingsDi v2, events menggunakan parameter variadic interface{} dan memerlukan meneruskan context ke setiap fungsi event. Event handler menerima data untyped yang perlu type assertion manual, sehingga sistem event rentan error dan sulit di-debug.
v3 memperkenalkan objek event bertipe dan menghapus persyaratan context. Event handler menerima objek event yang proper dengan data bertipe, sehingga sistem event lebih andal dan mudah digunakan.
v2:
runtime.EventsOn(ctx, "event-name", func(data ...interface{}) { // Tangani event})
runtime.EventsEmit(ctx, "event-name", data)v3:
app.Event.On("event-name", func(e *application.CustomEvent) { data := e.Data // Tangani event})
app.Event.Emit("event-name", data)Mengapa ini lebih baik:
...interface{}app.Event.On() dan app.Event.Emit() lebih intuitif daripada fungsi runtimev2 hanya mendukung satu window per aplikasi. Window dibuat saat startup dan semua operasi window dilakukan melalui fungsi runtime yang secara implisit menargetkan window tunggal itu.
v3 memperkenalkan dukungan multi-window native sebagai fitur inti. Setiap window adalah objek first-class dengan method dan lifecycle sendiri. Anda dapat membuat, mengelola, dan menghancurkan banyak window secara dinamis sepanjang lifetime aplikasi.
v2:
// Hanya single windowruntime.WindowSetSize(ctx, 800, 600)v3:
// Multi-window didukungwindow1 := app.Window.New()window1.SetSize(800, 600)
window2 := app.Window.New()window2.SetSize(1024, 768)Mengapa ini lebih baik:
go.mod:
module myapp
go 1.21
require ( github.com/wailsapp/wails/v3 v3.0.0-alpha.1)Perbarui:
go get github.com/wailsapp/wails/v3@latestgo mod tidyv2:
package main
import ( "embed" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" "github.com/wailsapp/wails/v2/pkg/options/windows")
//go:embed all:frontend/distvar assets embed.FS
func main() { app := NewApp()
err := wails.Run(&options.App{ Title: "My App", Width: 1024, Height: 768, AssetServer: &assetserver.Options{ Assets: assets, }, Bind: []interface{}{ app, }, Windows: &windows.Options{ WebviewIsTransparent: false, }, })
if err != nil { println("Error:", err.Error()) }}v3:
package main
import ( "embed" "github.com/wailsapp/wails/v3/pkg/application")
//go:embed frontend/distvar assets embed.FS
func main() { app := application.New(application.Options{ Name: "My App", Services: []application.Service{ application.NewService(&MyService{}), }, Assets: application.AssetOptions{ Handler: application.AssetFileServerFS(assets), }, })
app.Window.NewWithOptions(application.WebviewWindowOptions{ Title: "My App", Width: 1024, Height: 768, })
err := app.Run() if err != nil { panic(err) }}v2:
type App struct { ctx context.Context}
func NewApp() *App { return &App{}}
func (a *App) startup(ctx context.Context) { a.ctx = ctx // Inisialisasi}
func (a *App) Greet(name string) string { return "Hello " + name}v3:
type MyService struct { app *application.App}
func NewMyService(app *application.App) *MyService { return &MyService{app: app}}
func (s *MyService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { // Inisialisasi return nil}
func (s *MyService) Greet(name string) string { return "Hello " + name}
// Daftarkan setelah app dibuatapp := application.New(application.Options{})app.RegisterService(application.NewService(NewMyService(app)))v2:
func (a *App) DoSomething() { runtime.WindowSetTitle(a.ctx, "New Title") runtime.EventsEmit(a.ctx, "update", data) runtime.LogInfo(a.ctx, "Message")}v3:
func (s *MyService) DoSomething() { window := s.app.Window.Current() window.SetTitle("New Title")
s.app.Event.Emit("update", data)
s.app.Logger.Info("Message")}Generate bindings baru:
wails3 generate bindingsPerbarui import:
// v2import { Greet } from '../wailsjs/go/main/App'
// v3import { Greet } from './bindings/changeme/myservice'Perbarui penanganan event:
// v2import { EventsOn, EventsEmit } from '../wailsjs/runtime/runtime'
EventsOn("update", (data) => { console.log(data)})
EventsEmit("action", data)
// v3import { Events } from '@wailsio/runtime'
Events.On("update", (data) => { console.log(data)})
Events.Emit("action", data)v2 (wails.json):
{ "name": "myapp", "outputfilename": "myapp", "frontend:install": "npm install", "frontend:build": "npm run build", "frontend:dev:watcher": "npm run dev", "frontend:dev:serverUrl": "auto"}v3 (wails.json):
{ "name": "myapp", "frontend": { "dir": "./frontend", "install": "npm install", "build": "npm run build", "dev": "npm run dev", "devServerUrl": "http://localhost:5173" }}v2:
selection, err := runtime.OpenFileDialog(ctx, runtime.OpenDialogOptions{ Title: "Select File",})v3:
selection, err := app.Dialog.OpenFileWithOptions(&application.OpenFileDialogOptions{ Title: "Select File",}).PromptForSingleSelection()v2:
menu := menu.NewMenu()menu.Append(menu.Text("File", nil, []*menu.MenuItem{ menu.Text("Quit", nil, func(_ *menu.CallbackData) { runtime.Quit(ctx) }),}))v3:
menu := app.NewMenu()fileMenu := menu.AddSubmenu("File")fileMenu.Add("Quit").OnClick(func(ctx *application.Context) { app.Quit()})v2:
// Tidak tersedia di v2v3:
systray := app.SystemTray.New()systray.SetIcon(iconBytes)systray.SetLabel("My App")
menu := app.NewMenu()menu.Add("Show").OnClick(showWindow)menu.Add("Quit").OnClick(app.Quit)systray.SetMenu(menu)Problem: Error import setelah migrasi
Solusi:
# Regenerate bindingswails3 generate bindings
# Periksa direktori outputls frontend/bindingsProblem: ctx tidak tersedia
Solusi:
Simpan referensi app sebagai gantinya:
type MyService struct { app *application.App}
func NewMyService(app *application.App) *MyService { return &MyService{app: app}}Problem: runtime.WindowSetTitle() tidak ada
Solusi:
Gunakan method window langsung:
window := s.app.Window.Current()window.SetTitle("New Title")Problem: Events terdaftar tapi tidak diterima
Solusi:
Pastikan nama event cocok persis:
// Goapp.Event.Emit("my-event", data)
// JavaScriptOnEvent("my-event", handler) // Harus cocok persis# Developmentwails3 dev
# Buildwails3 build
# Generate bindingswails3 generate bindingsT: Bisakah saya menjalankan v2 dan v3 berdampingan?
J: Ya, keduanya menggunakan import path berbeda.
T: Apakah v3 production-ready?
J: v3 masih alpha/beta. Uji secara menyeluruh sebelum production.
T: Apakah v2 akan dirawat?
J: Ya, v2 akan menerima update kritis.
T: Berapa lama migrasi?
J: 1-4 jam untuk aplikasi tipikal.
Quick Start
Mulai dengan Wails v3.
Konsep Inti
Pahami arsitektur v3.
Bindings
Pelajari sistem bindings baru.
Contoh
Lihat contoh v3 lengkap.
Pertanyaan? Tanyakan di Discord atau buka issue.