Wails v3 目前仍是 Alpha,API 比 v2 新,也可能继续调整。学习时以 wails3 doctor 和当前安装版本为准。
当前验证环境:Windows,Wails CLI
v3.0.0-alpha.78,项目创建时间2026-04-28。
Wails 是什么
可以先把 Wails 理解成:
- 用 Go 写桌面端能力,比如窗口、菜单、文件选择、系统托盘、读写本地文件。
- 用 Vue / React / Svelte 等前端技术写界面。
- Wails 把 Go 方法生成成 JS/TS 绑定,前端可以像调用普通异步函数一样调用 Go。
- 打包时不会内置一个完整 Chrome,而是使用系统自带 WebView,所以体积通常比 Electron 小。
桌面应用里常说的“前端”和“后端”容易让新手误解。Wails 项目里的 Go 和 Vue 都跑在桌面应用内部,不是传统网站那种远程服务器后端。
v2 和 v3 的核心区别
与Wails v2 相比,v3 最值得先记住这些变化:
- 命令从
wails变成wails3。 - Go 包从
github.com/wailsapp/wails/v2/...变成github.com/wailsapp/wails/v3/pkg/application。 - 应用入口从
wails.Run(&options.App{})变成application.New(application.Options{})再app.Run()。 - 绑定方法不再写
Bind: []interface{}{},而是Services: []application.Service{ application.NewService(...) }。 - 前端调用 Go 的代码从
wailsjs/go/...变成frontend/bindings/...。 - v3 原生支持多窗口。
- v3 原生集成菜单、系统托盘、对话框等能力,整体 API 更偏
app.Window、app.Menu、app.Dialog这种管理器风格。
一句话:v2 像“配置一个 App 然后运行”,v3 像“先创建 app 对象,再用 app 管理窗口、菜单、事件、对话框”。
安装和检查环境
官方安装命令:
go install github.com/wailsapp/wails/v3/cmd/wails3@latest
检查是否安装成功:
wails3 version
wails3 doctor
如果 PowerShell 提示找不到 wails3,通常是 Go 的 bin 目录没有加入环境变量:
$env:USERPROFILE\go\bin
把这个目录加入用户 Path 后,重启终端。
本机安装时,latest 拉到的是 v3.0.0-alpha.78,并且 Go 自动切换到了 go1.25.9 工具链。Wails v3 安装页当前写 Go 1.25+,所以建议直接安装新版本 Go。
创建项目
查看模板:
wails3 init -l
创建 Vue + TypeScript 项目:
wails3 init -n wails3_beginner_demo -t vue-ts
运行开发模式:
cd wails3_beginner_demo
wails3 dev
构建 exe:
wails3 build
示例项目结构
重点先看这些文件:
wails3_beginner_demo/
├── main.go # Go 程序入口,创建 app、窗口、菜单、事件
├── greetservice.go # 暴露给前端调用的 Go 服务
├── frontend/
│ ├── src/App.vue # Vue 主页面
│ ├── public/style.css # 全局样式
│ ├── bindings/ # wails3 自动生成的前端调用 Go 的代码
│ └── package.json
├── build/config.yml # Wails 构建配置
├── Taskfile.yml # wails3 dev/build 背后的任务
└── go.mod
frontend/bindings 不要手写。你修改 Go 服务方法后,运行:
wails3 generate bindings
或者直接运行:
wails3 dev
Wails 通常会自动重新生成绑定。
Go 入口怎么写
示例入口在 wails3_beginner_demo/main.go。
核心流程是:
demoService := &DemoService{}
app := application.New(application.Options{
Name: "wails3_beginner_demo",
Services: []application.Service{
application.NewService(demoService),
},
Assets: application.AssetOptions{
Handler: application.AssetFileServerFS(assets),
},
})
demoService.setApp(app)
app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Wails v3 入门示例",
Width: 1000,
Height: 720,
URL: "/",
})
app.Run()
新手先记住 4 件事:
application.New创建应用。Services里注册 Go 服务,前端才能调用。app.Window.NewWithOptions创建窗口。app.Run()启动应用,并阻塞到应用退出。
前端调用 Go
Go 服务在 greetservice.go:
type DemoService struct {
app *application.App
}
func (s *DemoService) Greet(name string) string {
if name == "" {
name = "小白同学"
}
return fmt.Sprintf("你好,%s!这句话来自 Go。", name)
}
func (s *DemoService) Add(a int, b int) int {
return a + b
}
前端在 frontend/src/App.vue 调用:
import { DemoService } from '../bindings/changeme'
const result = await DemoService.Greet(name.value)
const total = await DemoService.Add(1, 2)
注意:前端调用 Go 方法是异步的,所以要用 await 或 .then()。
Go 主动通知前端
适合这些场景:
- Go 后台任务完成了,通知界面刷新。
- 监听系统事件后,把结果推给界面。
- 定时推送时间、状态、进度。
Go 里先注册事件类型:
application.RegisterEvent[string]("go-message")
Go 里发事件:
s.app.Event.Emit("go-message", "Go 主动发来一条事件消息")
Vue 里监听事件:
import { Events } from '@wailsio/runtime'
Events.On('go-message', (event: { data: string }) => {
message.value = event.data
})
选择规则:
- 前端需要一个明确返回值:用绑定方法。
- Go 要主动推送状态:用事件。
文件选择对话框
示例里 PickTextFile 会打开系统文件选择框,并读取前 800 个字符:
path, err := s.app.Dialog.OpenFile().
SetTitle("选择一个文本文件").
AddFilter("Text Files", "*.txt;*.md;*.log").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
这个能力来自 app.Dialog。新手不用自己写 Windows API,Wails 会调用系统原生对话框。
菜单
示例里创建了菜单:
menu := app.Menu.New()
fileMenu := menu.AddSubmenu("文件")
fileMenu.Add("打开文本文件").SetAccelerator("Ctrl+O").OnClick(func(ctx *application.Context) {
demoService.PickTextFile()
})
fileMenu.Add("退出").SetAccelerator("Ctrl+Q").OnClick(func(ctx *application.Context) {
app.Quit()
})
app.Menu.Set(menu)
窗口要显示应用菜单,建议加:
UseApplicationMenu: true
菜单项可以直接调用 Go 方法,也可以发事件给前端。
多窗口
v3 的一个重要升级就是原生多窗口。
示例里创建了主窗口和设置窗口:
app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Wails v3 入门示例",
URL: "/",
})
settingsWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "设置窗口",
Width: 520,
Height: 360,
Hidden: true,
URL: "/?window=settings",
})
显示设置窗口:
settingsWindow.Show()
settingsWindow.Focus()
设置窗口点右上角关闭时,默认会销毁窗口。销毁后再 Show() 旧窗口对象就不可靠,所以示例里用 RegisterHook 拦截关闭事件,把“关闭”改成“隐藏”:
settingsWindow.RegisterHook(events.Common.WindowClosing, func(event *application.WindowEvent) {
event.Cancel()
settingsWindow.Hide()
})
以后再点“设置窗口”,其实是把这个隐藏的窗口重新显示出来。
在 Vue 里通过 URL 参数区分当前窗口显示什么:
const isSettingsWindow = computed(() =>
new URLSearchParams(window.location.search).get('window') === 'settings'
)
这只是入门写法。真实项目里可以给不同窗口加载不同路由或不同页面。
系统托盘
Wails v3 内置系统托盘,这是 v3 相比 v2 很亮眼的升级点。v2 里通常要自己找第三方库,比如 systray,还要处理它和 Wails 生命周期的配合;v3 可以直接用 app.SystemTray。
示例里已经加了托盘功能:
- 单击托盘图标:显示 / 隐藏主窗口。
- 右键托盘图标:打开托盘菜单。
- 托盘菜单支持:显示主窗口、隐藏到托盘、打开设置窗口、发事件给前端、退出应用。
- 前端页面里也有“隐藏到托盘”按钮。
Go 里嵌入托盘图标:
//go:embed build/windows/icon.ico
var trayIcon []byte
创建托盘菜单:
trayMenu := app.Menu.New()
trayMenu.Add("显示主窗口").OnClick(func(ctx *application.Context) {
mainWindow.Show().Focus()
})
trayMenu.Add("隐藏到托盘").OnClick(func(ctx *application.Context) {
demoService.HideMainWindowToTray()
})
trayMenu.Add("设置窗口").OnClick(func(ctx *application.Context) {
demoService.ShowSettingsWindow()
})
trayMenu.AddSeparator()
trayMenu.Add("退出").OnClick(func(ctx *application.Context) {
app.Quit()
})
创建系统托盘:
tray := app.SystemTray.New()
tray.SetIcon(trayIcon).
SetMenu(trayMenu).
AttachWindow(mainWindow)
tray.SetTooltip("Wails v3 入门示例:单击显示/隐藏,右键打开菜单")
这里最重要的是 AttachWindow(mainWindow)。绑定窗口后,Wails 会给托盘一个默认行为:点击托盘图标时切换窗口显示和隐藏。如果设置了菜单,右键会显示菜单。
隐藏主窗口的方法在 greetservice.go:
func (s *DemoService) HideMainWindowToTray() {
if s.mainWindow == nil {
return
}
s.mainWindow.Hide()
s.app.Event.Emit("go-message", "主窗口已隐藏,可以从系统托盘图标恢复")
}
前端调用:
function hideToTray() {
DemoService.HideMainWindowToTray()
}
需要注意:托盘不是“关闭窗口”。如果你点窗口右上角关闭,应用可能会退出;如果你想做微信、QQ 那种“点关闭只是隐藏到托盘”,还需要额外监听窗口关闭事件并拦截关闭行为。这个示例先演示最核心的托盘 API。
开发时的常用命令
在示例项目目录运行:
cd wails3_beginner_demo
安装前端依赖:
cd frontend
npm install
cd ..
生成绑定:
wails3 generate bindings
开发运行:
wails3 dev
构建:
wails3 build
只检查 Go 是否能编译:
go test ./...
只检查前端:
cd frontend
npm run build
小白学习路线
建议按这个顺序学:
- 先跑起来:
wails3 dev。 - 改
App.vue上的一段文字,看热更新是否生效。 - 改
DemoService.Greet的返回值,看前端调用 Go 的结果变化。 - 新增一个 Go 方法,比如
Multiply(a, b int) int。 - 运行
wails3 generate bindings。 - 在 Vue 里调用
DemoService.Multiply。 - 学事件:让 Go 每隔几秒发一个消息给前端。
- 学系统能力:文件选择、保存文件、菜单、系统托盘、窗口。
- 最后再做复杂功能:SQLite、本地配置、关闭到托盘、自动更新。
不要一开始就上 Pinia、路由、UI 组件库、SQLite。先把“Vue 调 Go”和“Go 通知 Vue”这两条线打通。
常见坑
wails3 找不到
Go bin 没进 Path。Windows 常见路径:
C:\Users\你的用户名\go\bin
修改 Go 方法后前端找不到
重新生成绑定:
wails3 generate bindings
然后重启前端类型检查或 wails3 dev。
frontend/dist 不存在
Go 里有:
//go:embed all:frontend/dist
var assets embed.FS
所以直接 go test ./... 或 go build 前,要先让前端构建出 dist:
cd frontend
npm run build
cd ..
go test ./...
或者直接用:
wails3 build
文档和当前版本 API 不一致
v3 仍是 Alpha,确实会遇到。处理方式:
- 先看本机
wails3 version。 - 再看
go/pkg/mod/github.com/wailsapp/wails/v3@版本号/pkg/application里的源码。 - 以
wails3 generate bindings和wails3 build的实际结果为准。
参考文档
- Wails v3 首页:https://v3alpha.wails.io/
- 安装文档:https://v3alpha.wails.io/quick-start/installation/
- 菜单文档:https://v3alpha.wails.io/features/menus/application/
- 文件对话框:https://v3alpha.wails.io/features/dialogs/file/
- 多窗口文档:https://v3alpha.wails.io/features/windows/multiple/
- Go 包文档中的 SystemTray API:https://pkg.go.dev/github.com/wailsapp/wails/v3/pkg/application
- Go 包文档:https://pkg.go.dev/github.com/wailsapp/wails/v3