关于 wails
为使用 Go 后端和平台 WebView 前端构建桌面应用提供 Wails 指南,覆盖稳定的 v2 与 Alpha 阶段的 v3。该技能强调先确认版本,避免混用不兼容的 API。
主要功能
- 01介绍 Go 与 Web 前端桌面应用
- 02分别说明 Wails v2 与 v3
- 03指导识别版本及对应 API
- 04涵盖绑定、事件与构建流程
- 05说明 v3 内嵌 MCP 服务能力
适用场景
- 01选择新项目使用的 Wails 版本
- 02排查 v2 与 v3 API 混用
- 03设计 Go 后端与前端绑定
- 04使用 Wails MCP 驱动应用测试
MCP 相关
指南说明 Wails v3 可通过设置 WAILS_MCP=1 在应用中嵌入 MCP 服务。MCP 客户端可借此控制窗口、检查 DOM 并调用绑定方法。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ spf13/ go-skills --skill wails安装后在 Claude Code 中输入/wails确认已加载。
附带文件.claude-plugin/plugin.json
详细介绍
Wails:使用 Go 构建桌面应用
Wails 使用 Go 后端和在平台 WebView 中渲染的 Web 前端(macOS/Linux 上为 WebKit,Windows 上为 WebView2)构建原生桌面应用。无需捆绑 Chromium——二进制文件只有几 MB,而不是几百 MB。
两个主要版本并存,API 互不兼容。 v2 稳定。v3(截至 2026 年年中)处于 alpha 后期:API 已相当稳定,也有生产应用在使用,但发布版本仍是 nightly v3.0.0-alpha2.x 标签,具体细节仍在变动。最常见的失败原因——尤其对大模型而言——是混用 v2 和 v3 代码。每个回答都必须纯粹使用 v2 或 v3。
步骤 0——检测版本(必须)
编写任何代码之前,先根据项目确定版本。不要根据用户的措辞猜测。
| 信号 | v2 | v3 |
|---|---|---|
| go.mod 导入 | github.com/wailsapp/wails/v2 | github.com/wailsapp/wails/v3 |
| CLI | wails(wails dev、wails build) | wails3(wails3 dev、wails3 build) |
| 构建编排 | 不透明,由 wails.json 驱动 | Taskfile.yml(go-task),透明 |
| 入口点 | wails.Run(&options.App{...}) | application.New(application.Options{...}) |
| Go→JS 暴露方式 | Bind: []interface{}{app} | Services: []application.Service{...} |
| Runtime 调用 | runtime.EventsEmit(ctx, ...),并存储上下文 | 调用 app / window 上的方法——无需传递上下文 |
| 前端导入 | wailsjs/go/main/App | frontend/bindings/<import-path>/<service> |
**尚无代码的新项目:**如果用户需要稳定性且不希望频繁变动,默认使用 v2;如果用户接受 alpha 状态,则建议为新的长期应用使用 v3——其架构更完善(多窗口、服务、透明构建),并且是未来的发展方向。用一句话说明取舍,并在 go.mod 中固定确切的 alpha 版本。绝不要将 v3 描述为稳定版。
**如果现有代码中同时出现两列的 API,**这就是问题所在——先指出这一点,再进行其他操作。
Wails v2(稳定版)
标准 main.go
//go:embed all:frontend/dist
var assets embed.FS
func main() {
app := NewApp()
err := wails.Run(&options.App{
Title: "myapp",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{Assets: assets},
OnStartup: app.startup,
Bind: []interface{}{app},
})
if err != nil {
log.Fatal(err)
}
}
embed 指令为 all:frontend/dist——必须使用 all: 前缀,才能包含 Vite 生成的以下划线开头的文件。
上下文捕获模式
v2 Runtime 中的所有功能都需要 OnStartup 传入的上下文。保存该上下文;这是每个 v2 应用的关键惯用模式:
type App struct {
ctx context.Context
}
func (a *App) startup(ctx context.Context) {
a.ctx = ctx // required for all runtime calls
}
func (a *App) OpenFile() (string, error) {
return runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{})
}
- 绝不要在
OnStartup触发前调用runtime.*——此时上下文尚不存在。这是典型的 v2 启动崩溃原因。 - 此处的
runtime指github.com/wailsapp/wails/v2/pkg/runtime——用于窗口控制、对话框、事件、剪贴板和菜单。
绑定与事件
- 绑定结构体上的导出方法会成为
wailsjs/go/main/App下的异步 JS 函数。Go 的(value, error)返回值会转换为已兑现/已拒绝的 Promise。 - Go→JS:使用
runtime.EventsEmit(a.ctx, "name", data);JS 端从wailsjs/runtime导入EventsOn("name", cb)。 - 绑定结构体应保持为精简的适配器——业务逻辑放在不导入 Wails 的独立包中(规则与 cobra-viper 技能中的
cmd/相同)。
v2 常见错误
- 在启动前调用 Runtime 函数(上下文为 nil/无效)。
- embed 指令缺少
all:——资源会在生产构建中被静默遗漏。 - 编辑生成的
wailsjs/文件——每次运行wails dev/wails build时都会重新生成。 - 按值绑定结构体(
Bind: []interface{}{App{}})——应绑定指针,以便状态得以保留。
Wails v3(Alpha)
标准 main.go
//go:embed all:frontend/dist
var assets embed.FS
func main() {
app := application.New(application.Options{
Name: "myapp",
Assets: application.AssetOptions{
Handler: application.AssetFileServerFS(assets),
},
Services: []application.Service{
application.NewService(NewGreetService()),
},
})
app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "myapp",
})
if err := app.Run(); err != nil { // blocks; must run on the main goroutine
log.Fatal(err)
}
}
使用 Manager API 风格——app.Window.NewWithOptions、app.Event.On、app.Menu.New()、app.SystemTray.New()。早期 alpha 版本使用扁平方法(app.NewWebviewWindowWithOptions、app.OnEvent);教程和大模型训练数据中充斥着这些旧方法。如果某个方法不存在,它很可能已移至某个 Manager 下——请查阅变更日志,而不是凭空编造。
服务:v3 绑定模型
服务取代了 v2 的 Bind。服务是普通结构体;导出的方法会被绑定,另外还有两个可选的生命周期方法可接入应用:
type NoteService struct {
db *sql.DB
mu sync.RWMutex
}
func (s *NoteService) ServiceStartup(ctx context.Context, opts application.ServiceOptions) error {
// open resources, run migrations, start background goroutines governed by ctx
return nil // returning error aborts app startup
}
func (s *NoteService) ServiceShutdown() error {
return s.db.Close() // services shut down in reverse registration order
}
func (s *NoteService) Save(n Note) error { ... } // callable from JS
- 服务是由所有窗口共享的单例——使用互斥锁保护可变状态。
- 依赖通过普通构造函数传入(
application.NewService(NewNoteService(db, logger)))——无需 DI 框架。 - 服务可通过实现
http.Handler提供 HTTP 服务,并使用application.NewServiceWithOptions(svc, application.ServiceOptions{Route: "/files"})注册。 - 与 v2 一样遵循精简原则:服务是适配器;领域逻辑放在不依赖 Wails 的包中,便于作为普通 Go 代码进行测试。
绑定、事件和窗口
- 使用
wails3 generate bindings生成前端绑定(wails3 dev会自动运行)。从frontend/bindings/<full-go-import-path>/<service>导入,而不是从wailsjs/导入。 - 事件:
app.Event.Emit("name", data)/app.Event.On("name", handler);操作系统级事件通过app.Event.OnApplicationEvent(events.Common.ApplicationStarted, ...)处理。可取消的钩子:window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { e.Cancel() })——这是标准的“关闭前确认”模式。 - 多窗口是 v3 的一等功能:可根据需要创建任意数量的
WebviewWindow;每个窗口都可独立配置。
构建系统
v3 构建由 go-task 通过 Taskfile.yml 编排——图标生成、清单和打包都是可见、可编辑的任务步骤,而不是黑箱。通过编辑任务进行自定义,不要与 CLI 较劲。平台打包命令:wails3 package(macOS 上生成 .app/dmg,Windows 上生成 NSIS 包,Linux 上生成 AppImage/deb)。
Agentic 额外优势:使用 WAILS_MCP=1 构建会嵌入 MCP 服务,让大模型 Agent(包括 Claude)能够操控正在运行的应用——控制窗口、检查 DOM、调用绑定方法——这对开发期间的自动化测试非常有用。
v3 Alpha 使用规范
- 在 go.mod 中固定 alpha 版本,并谨慎升级,同时阅读变更日志(
v3.wails.io/changelog)——nightly alpha 版本可能会重命名 API。 - 如果 v3 API 调用编译失败且无法明显看出修复方法,请先查看当前文档再进行猜测——由于 alpha 版本的特性,训练数据会滞后。
版本混用迹象(发现即拒绝)
- 在 v3 项目中使用
runtime.EventsEmit(ctx, ...)或导入pkg/runtime→ 这是 v2 API;应使用app.Event.Emit。 - 在 v2 项目中使用
application.New(...)或pkg/application→ 这是 v3 API;应使用wails.Run(&options.App{...})。 - v3 前端导入
wailsjs/go/...(应使用frontend/bindings/...),或反之。 - 在 v3 项目中运行
wails dev,或在 v2 项目中运行wails3 dev。 - 在
application.Options中包含Bind:字段,或在options.App中包含Services:——这些字段各自只属于对应的版本。
两个版本通用的原则
- 前端输入不可信:绑定方法中的参数必须像 HTTP 处理器一样进行验证;绝不能根据原始前端字符串构建 shell 命令或文件路径。
- 绑定方法应返回错误(错误会以消息形式成为 Promise 拒绝)——不要 panic,也不要吞掉错误。
- 密钥保留在 Go 端;
frontend/dist中发布的任何内容都可被用户读取。 - 绑定方法中的长时间运行任务会阻塞 JS Promise,但不会阻塞 UI——应发送进度事件,而不是让前端轮询。