本文永久链接https://tonybai.com/2026/08/04/wails-v3-go-desktop-framework

大家好,我是Tony Bai。

【导读】

如果你用 Go 写过桌面应用,大概率听过 Wails——它让 Go 开发者可以用 HTML、CSS、JS 这套最熟悉的前端工具链,调用系统原生 WebView,做出媲美 Electron 的跨平台桌面应用,却不必打包一整个 Chromium。

北京时间 2026 年 8 月 2 日,Wails 团队正式发布了 Wails v3 Beta。这不是一次小版本迭代,而是自 2023 年 1 月立项以来,历时三年多打磨出的一次架构级重写:应用不再只有一个隐藏在配置项背后的 Run() 函数,而是拥有了显式的应用、窗口、服务对象模型;前端绑定不再依赖“先编译再反射”的旧套路,而是通过静态源码分析直接生成;构建系统也从一个黑盒命令,变成了可读、可改、可调试的 Taskfile。

这篇文章将带你梳理 Wails v3 Beta 到底带来了什么、为什么要做这么大的重构,以及如何用几行代码跑起你的第一个 v3 应用。

【文章要点】

  • Wails v3 Beta 于 2026 年 8 月 2 日正式发布,桌面端 API 已稳定,已有团队在生产环境使用,但仍建议在正式迁移前充分测试;
  • 核心变化有五个:显式的应用生命周期 API、原生多窗口支持、基于静态分析的 Go 服务与自动生成绑定、可检查可扩展的 Taskfile 构建系统、更扎实的跨平台基线(含实验性 iOS / Android 支持);
  • 从 v2 迁移到 v3 是一次真正的移植工作,官方提供了详细的迁移指南和功能对照表,而非简单的版本号升级;
  • Wails 项目的 GitHub Star 数从 2021 年的约 4000,涨到 2022 年 v2 发布时的约 10300,如今已超过 35000;
  • 项目从“完美主义式”发版策略,转向了 2024 年底开始的“每日发版”策略,加速了 Beta 的到来;
  • 安装方式很简单:go install github.com/wailsapp/wails/v3/cmd/wails3@latest,然后 wails3 setupwails3 init 即可上手。


Wails 是什么

对没接触过 Wails 的读者简单科普一下:Wails 让 Go 开发者可以用自己已经很熟悉的 Web 前端技术栈(HTML/CSS/JS,以及 Vue、React、Svelte 等框架)来写桌面应用界面,后端逻辑全部用 Go 编写,两者之间通过自动生成的绑定互相调用。

和 Electron 最大的不同在于,Wails 调用的是操作系统自带的原生 WebView 组件(macOS 上是 WKWebView,Windows 上是 WebView2,Linux 上是 WebKitGTK),而不是把一整个 Chromium 内核塞进安装包里。这意味着同样的应用,Wails 打出来的包体积要小得多,启动速度也更快。

Wails v2 于 2022 年 9 月 22 日发布,带来了基于 Vite 的热更新开发体验、窗口与菜单管理、微软 WebView2 组件、Go 结构体到 TypeScript 类型的自动生成、NSIS 安装包制作、代码混淆构建等一整套能力,是目前生产环境中依旧稳定可靠的版本。

为什么要做 v3:v2 的三大痛点

Lea Anthony 在 2023 年 1 月发布的规划文章《The Road to Wails v3》里,坦诚地列出了 v2 架构里拖累项目的三个问题。

第一,应用 API 太“声明式”,多窗口成了硬伤

v2 的应用层 API 事实上只有一个函数——wails.Run(),把一大堆配置项塞进去,框架帮你把一切都做好。这种写法上手简单,但也意味着开发者拿不到主窗口的句柄,想直接操作窗口只能求助另一套“运行时 API”(Runtime API),而这套 API 又必须传入一个 context.Context 才能工作,新手极容易踩坑。更关键的是,Runtime API 从设计之初就是为单窗口场景服务的,而“支持多窗口”恰恰是 GitHub 上呼声最高的功能请求。

第二,绑定生成靠“先编译、后反射”的取巧办法

v2 生成前端可调用绑定的方式,是先用特殊标志编译出一个二进制文件,再运行它、用反射分析出到底绑定了哪些方法——这是一个先有鸡还是先有蛋的怪圈:不生成绑定就编译不出完整应用,不编译应用又生成不出绑定。

第三,构建系统是个黑盒

执行一次 wails build,背后要做的事情多达十几步:编译后端二进制、生成绑定、安装前端依赖、构建前端资源、处理图标、编译最终二进制、macOS 下用 lipo 合并双架构胖二进制、UPX 压缩、打包应用包或 NSIS 安装包……全部逻辑封装在框架内部,想自定义某一步几乎无从下手,出了问题也很难调试。

这三点,构成了 v3 重写的直接动机。

v3 Beta 到底新在哪:五大变化逐一拆解

1. 显式的应用生命周期 API

v3 用一套显式的对象模型,取代了 v2 那个包罗万象的 wails.Run(...)。现在你需要创建一个应用实例、注册服务、创建窗口,然后直接对这些对象进行操作——窗口相关的行为归窗口对象管,应用级别的行为归应用对象管,不再需要到处传递隐式的 Context。

这种模式对多窗口应用天然友好,也更符合大型代码库的可测试性和可维护性诉求。官方在文章中特别提到,Alpha 阶段的开发者对这种显式模型反馈非常积极——代码更容易读懂,归属关系更清晰,复杂应用也有了在框架内“自然生长”的空间,而不用和框架“打架”。

2. 原生多窗口支持

多窗口不再是workaround,而是 v3 的核心能力之一。窗口拥有独立的生命周期,可以在运行时随意创建、管理、关闭,这为编辑器、检查器、偏好设置面板、工具窗口等一切需要多个独立界面的桌面应用铺平了道路。

3. Go 服务与静态分析生成的绑定

v3 用“服务(Service)”取代了 v2 那种松散的绑定模式。业务逻辑依然是普通的 Go 代码,但服务和前端之间的边界变得非常清晰:生成的绑定会按照应用和服务的结构组织起来,方便查找和使用。

更关键的是绑定生成方式的变化:v3 改用静态源码分析,而不是运行已编译程序再靠反射“猜”绑定关系。这不仅解决了 v2 那种先有鸡还是先有蛋的怪圈,还带来一个额外好处——生成器现在能够保留开发者写在代码里的信息,比如注释和有意义的参数名,因此生成出来的前端 TypeScript API 更加丰富、可读性更强。

值得一提的是,服务现在还可以捆绑自己的前端资源和脚本,也就是说一个能力可以同时拥有自己的后端 API、前端 JS/UI,以及和宿主应用的集成点。这为将来的 Wails 插件生态打开了空间:装一个插件、挂载它的服务,就能直接用上一整套开箱即用的功能,而不必再手动拼装一堆零散的绑定和前端依赖。

官方明确说明,通用插件系统本身并不在这次 Beta 范围内,但 v3 的架构已经让这个方向变得可行——这在 v2 的绑定模型下是做不到的。

4. 可检查、可扩展的构建系统

v3 把构建流程彻底摊开在明面上。项目采用统一的目录结构,配合基于 TaskTaskfile.yml 构建配置——熟悉 Makefile 的开发者会很快上手。日常仍然可以用 wails3 build 一键完成所有事情,但如果你想定制构建流程,直接编辑 Taskfile.yml 就行,图标生成、压缩、打包这些原子操作依然由 Wails CLI 内置提供,不需要额外装一堆外部工具。

5. 更扎实的跨平台基线

Beta 版本支持 Windows(amd64/arm64)、macOS(Intel/Apple Silicon)、Linux(amd64/arm64)。Linux 端默认技术栈是 GTK4 + WebKitGTK 6.0,GTK3 会作为遗留选项在整个 v3.0 系列里继续保留。此外,v3 还带来了实验性的 iOS 和 Android 支持,可以探索尝鲜,但暂不在桌面 Beta 的兼容性承诺范围内。

除此之外,这次发布也补上了不少“日常体验”层面的工作:更完善的平台行为、更强大的窗口模型、更清晰的诊断信息,以及带校验和与来源证明(provenance)的发布产物。

从“完美主义”到“每日发版”:一段坦诚的开发历程

v3 的第一个 Alpha Tag 打在 2023 年 1 月 18 日,到 2026 年 8 月的 Beta 发布,走了三年半。这中间发生了什么?

2024 年 12 月,团队发布了 Alpha 10,同时公开做了一次坦诚的复盘:早期团队陷入了“完美主义陷阱”——总想在每次发版前把每一个 bug 都修完、每一个功能都打磨到位,但软件从来不存在“完全无 bug”的状态,一味等待只会拖慢改进落地的速度。

于是团队从 Alpha 10 开始转向每日发版策略:当天合并的改动,当天就会发布出去。

这么做的好处是明摆着的——bug 修复和新功能能更快触达用户、反馈循环更短、Beta 之路走得更快、整个开发过程也更透明。

团队同时开放了 Alpha 阶段的 bug 报告渠道,把标记为 “Ready for Work” 的 issue 开放给社区贡献者认领,并特别强调:测试 PR 是当前最有价值的贡献方式之一,不需要多深的代码功底,光是在不同环境下验证改动能不能正常工作,就已经很有价值。

这次“不装了”式的坦诚,也延续到了 Beta 发布文章里。作者提到,Wails 的 Star 数从 2021 年 v2 首个 Beta 发布时的约 4000,涨到 2022 年 v2 正式发布时的约 10300,如今已经超过 35000。

用户体量的增长意味着更多人依赖发版决策、更多贡献者需要清晰的参与路径,好的项目治理不再只是“做完下一个功能”那么简单。作者坦承自己在过去没能及时跟上项目增长所要求的流程演进,并给出了具体的改进措施:明确的 Beta / RC / GA 里程碑、更清晰的兼容性承诺、更新后的安全策略,以及一套用于评审公开行为变更和新能力的 WEP(Wails Enhancement Proposal)流程

上手实战:五分钟跑起你的第一个 Wails v3 应用

说了这么多架构层面的变化,不如直接上手感受一下。以下步骤基于官方 Beta 文档,环境要求 Go 1.25 及以上。

1. 安装 CLI 并跑一次环境体检

go install github.com/wailsapp/wails/v3/cmd/wails3@latest
wails3 setup

wails3 setup 会检查本地开发环境,并引导你配置 Wails 所需的各项依赖(比如 Linux 下的 WebKitGTK)。

2. 初始化一个项目

wails3 init -n myapp
cd myapp
wails3 dev

默认模板是 Vanilla + Vite(HTML/CSS/TypeScript)。如果你更喜欢 React、Vue、Svelte,可以加上 -t react-t vue-t svelte 参数;纯 JavaScript 版本用 -t vanilla-js-t react-js。想看全部可选模板,执行 wails3 init -l

初始化完成后目录大致长这样:

myapp/
├── README.md
├── Taskfile.yml                 # 构建任务定义
├── build/                       # 构建相关配置
│   ├── Taskfile.yml
│   ├── android/
│   ├── appicon.icon/
│   ├── appicon.png
│   ├── config.yml
│   ├── darwin/
│   ├── docker/
│   ├── ios/
│   ├── linux/
│   └── windows/
├── frontend/                    # 前端代码
│   ├── Inter\ Font\ License.txt
│   ├── index.html
│   ├── package.json
│   ├── public/
│   ├── src/
│   ├── tsconfig.json
│   └── vite.config.ts
├── go.mod
├── go.sum
├── greetservice.go              # 一个示例服务
└── main.go                      # 应用入口

11 directories, 14 files

运行wails3 dev后,wails会下载依赖、构建并运行一个脚手架程序:

$wails3 dev
Wails v3.0.0-beta.2  Build
task: [darwin:common:go:mod:tidy] go mod tidy
task: [darwin:common:generate:icons] wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico -iconcomposerinput appicon.icon -macassetdir darwin
task: [darwin:common:generate:bindings] wails3 generate bindings -f '-buildvcs=false -gcflags=all="-l"' -clean=true -ts -i
Wails v3.0.0-beta.2  Generate Bindings
  Loading packages... (0s)task: [darwin:common:install:frontend:deps:npm] npm install
 INFO  Processed: 238 Packages, 1 Service, 1 Method, 0 Enums, 0 Models, 1 Event in 39.01382603s.                                  
 INFO  Output directory: /Users/tonybai/test/go/wails-v3/myapp/frontend/bindings

added 24 packages in 41s

9 packages are looking for funding
  run `npm fund` for details
task: [darwin:common:frontend:run:npm] npm run build:dev -q

> frontend@0.0.0 build:dev
> vite build --minify false --mode development

vite v8.2.0 building client environment for development...
 35 modules transformed.
computing gzip size...
dist/index.html                 3.20 kB  gzip:  1.23 kB
dist/assets/index-Bgw7f08b.js  89.57 kB  gzip: 23.05 kB

 built in 73ms
task: [darwin:build:native] go build -buildvcs=false -gcflags=all="-l" -o "bin/myapp"

Need documentation? Run: wails3 docs
    If Wails is useful to you or your company, please consider sponsoring the project: wails3 sponsor
task: [darwin:run] mkdir -p "bin/myapp.dev.app/Contents/MacOS"
task: [darwin:run] mkdir -p "bin/myapp.dev.app/Contents/Resources"
task: [darwin:run] cp build/darwin/icons.icns "bin/myapp.dev.app/Contents/Resources"
task: [darwin:run] if [ -f build/darwin/Assets.car ]; then
  cp build/darwin/Assets.car "bin/myapp.dev.app/Contents/Resources"
fi

task: [darwin:run] cp "bin/myapp" "bin/myapp.dev.app/Contents/MacOS"
task: [darwin:run] cp "build/darwin/Info.dev.plist" "bin/myapp.dev.app/Contents/Info.plist"
task: [darwin:run] codesign --force --deep --sign - "bin/myapp.dev.app"
task: [darwin:run] "bin/myapp.dev.app/Contents/MacOS/myapp"
task: [common:install:frontend:deps:npm] npm install

added 1 package in 1s

9 packages are looking for funding
  run `npm fund` for details
task: [common:frontend:dev:npm] npm run dev -- --port 9245 --strictPort

> frontend@0.0.0 dev
> vite --port 9245 --strictPort

2:49PM INF Build Info: Wails=v3.0.0-beta.2 Compiler=go1.26.0 DefaultGODEBUG=cryptocustomrand=1,tlssecpmlkem=0,urlstrictcolons=0 CGO_CPPFLAGS= CGO_CXXFLAGS= -compiler=gc CGO_ENABLED=1 CGO_CFLAGS=-mmacosx-version-min=12.0 CGO_LDFLAGS=-mmacosx-version-min=12.0 GOARCH=amd64 GOOS=darwin GOAMD64=v1 -buildmode=exe -gcflags=all=-l
2:49PM INF Platform Info: ID=24G231 Name=MacOS Version=15.7.1 Branding=Sequoia
2:49PM INF AssetServer Info: middleware=true handler=true devServerURL=http://localhost:9245
2:49PM INF Waiting for frontend dev server to start... url=http://localhost:9245
2:49PM INF Retrying...

  VITE v8.2.0  ready in 2049 ms

    Local:   http://127.0.0.1:9245/
2:49PM INF Retrying...
2:49PM INF Connected to frontend dev server!

之后就会看到一个如下的页面出现在你眼前:

输入你要Greeting的名字,程序便可以正常运作。

3. 感受一下“服务即后端 API”

打开 greetservice.go,可以看到一个再普通不过的 Go 结构体:

package main

type GreetService struct{}

func (g *GreetService) Greet(name string) string {
	return "Hello " + name + "!"
}

任何导出的方法(首字母大写)都会被静态分析器扫描到,自动生成对应的 TypeScript 绑定。在 main.go 里,你只需要把服务注册进应用配置:

package main

import (
	"embed"

	"log"
	"time"

	"github.com/wailsapp/wails/v3/pkg/application"
)

// Wails uses Go's `embed` package to embed the frontend files into the binary.
// Any files in the frontend/dist folder will be embedded into the binary and
// made available to the frontend.
// See https://pkg.go.dev/embed for more information.

//go:embed all:frontend/dist
var assets embed.FS

func init() {
	// Register a custom event whose associated data type is string.
	// This is not required, but the binding generator will pick up registered events
	// and provide a strongly typed JS/TS API for them.
	application.RegisterEvent[string]("time")
}

// main function serves as the application's entry point. It initializes the application, creates a window,
// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and
// logs any error that might occur.
func main() {

	// Create a new Wails application by providing the necessary options.
	// Variables 'Name' and 'Description' are for application metadata.
	// 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files.
	// 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances.
	// 'Mac' options tailor the application when running an macOS.
	app := application.New(application.Options{
		Name:        "myapp",
		Description: "A demo of using raw HTML & CSS",
		Services: []application.Service{
			application.NewService(&GreetService{}), // Bind服务
		},
		Assets: application.AssetOptions{
			Handler: application.AssetFileServerFS(assets),
		},
		Mac: application.MacOptions{
			ApplicationShouldTerminateAfterLastWindowClosed: true,
		},
	})

	// Create a new window with the necessary options.
	// 'Title' is the title of the window.
	// 'Mac' options tailor the window when running on macOS.
	// 'BackgroundColour' is the background colour of the window.
	// 'URL' is the URL that will be loaded into the webview.
	app.Window.NewWithOptions(application.WebviewWindowOptions{
		Title: "Window 1",
		// Window sized to the golden ratio (1000 / 618 ≈ 1.618).
		Width:  1000,
		Height: 618,
		Mac: application.MacWindow{
			InvisibleTitleBarHeight: 50,
			Backdrop:                application.MacBackdropTranslucent,
			TitleBar:                application.MacTitleBarHiddenInset,
		},
		BackgroundColour: application.NewRGB(6, 7, 15),
		URL:              "/",
	})

	// Create a goroutine that emits an event containing the current time every second.
	// The frontend can listen to this event and update the UI accordingly.
	go func() {
		for {
			now := time.Now().Format(time.RFC1123)
			app.Event.Emit("time", now)
			time.Sleep(time.Second)
		}
	}()

	// Run the application. This blocks until the application has been exited.
	err := app.Run()

	// If an error occurred while running the application, log it and exit.
	if err != nil {
		log.Fatal(err)
	}
}

前端这边,直接从自动生成的绑定里导入服务,像调用本地异步函数一样调用 Go 方法即可(下面代码已做了简化):

import { GreetService } from "../bindings/changeme";

window.greet = async () => {
	const name = document.getElementById("name").value;
	if (!name) return;

	try {
		const result = await GreetService.Greet(name);
		document.getElementById("result").innerText = result;
	} catch (err) {
		console.error(err);
	}
};

执行 wails3 dev 后,Go 代码的改动会自动触发重新编译并重启应用,前端代码的改动则直接热更新,不需要重启——这套开发体验和 v2 保持了一致的顺滑。

4. 加个多窗口试试

如果想直观感受一下 v3 引以为傲的多窗口能力,可以在 main.go 里再开一个窗口,并绑定关闭事件:

package main

import (
	"github.com/wailsapp/wails/v3/pkg/application"
	"github.com/wailsapp/wails/v3/pkg/events"
)

func main() {
	app := application.New(application.Options{Name: "MultiWindowDemo"})

	mainWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
		Title: "主窗口",
		URL:   "/",
	})
	mainWindow.OnWindowEvent(events.Common.WindowClosing, func(e *application.WindowEvent) {
		app.Quit()
	})

	inspector := app.Window.NewWithOptions(application.WebviewWindowOptions{
		Title: "检查器",
		Width: 480, Height: 640,
		URL: "/inspector.html",
	})
	_ = inspector

	app.Run()
}

两个窗口各自拥有独立的生命周期,互不依赖 Context 传递——这正是 v3 想解决的核心痛点之一。

5. 打包上线

wails3 build

会依次完成 Go 代码优化编译、前端资源生产构建(压缩)、生成对应平台的原生可执行文件,产物默认放在 bin/ 目录下。想自定义打包细节,直接改 Taskfile.yml 里对应的 task 即可。

从 v2 迁移到 v3,该注意什么

需要提醒的是:v3 是一次大版本升级,迁移是一次真正的移植工作,而不是改个 import 路径那么简单。 主要涉及的概念性变化包括:

  • 应用与窗口的生命周期模型;
  • 用“服务”取代了绑定到 Context 上的旧式绑定;
  • 直接的应用 / 窗口 API 取代了 v2 的 Runtime 包;
  • 前端绑定需要重新生成。

官方发布了详细的 v2 到 v3 迁移指南,里面包含功能对照表和测试清单,是目前 Beta 阶段官方支持的迁移路径。团队也明确建议:不要指望任何 v2 项目能“无脑转码”成功,一定要逐个测试、有意识地移植 Runtime 调用,并且在新应用真正就绪之前,让 v2 版本继续保留在生产环境中。

值得关注的是,官方还透露正在评估一个实验性的迁移辅助工具,但目前尚未包含在 Beta 里,团队表示会先用真实的 v2 项目验证充分之后,才会正式推荐使用。

另外一个细节是,Beta 阶段的官方文档只维护英文版本作为唯一可信源,暂不接受翻译 PR——要等 API 和工作流最终稳定之后,翻译工作才会重新启动,避免译者跟着文档反复返工。

小结

Wails v3 目前仍是 Beta,而非最终的 3.0 正式版:桌面端 API 已经稳定,也已经有团队在生产环境中使用,但官方仍然建议在正式切换前做充分测试。Wails v2 依旧是当前的稳定版本,会继续接收修复更新,短期内并不会被“抛弃”。

对于 Go 语言的桌面应用开发者来说,v3 带来的显式对象模型、原生多窗口、静态分析绑定和透明构建系统,几乎是针对 v2 时代最常被吐槽的几个点做的定向修复,方向感很强。如果你之前因为“不支持多窗口”、“Runtime API 太绕”而对 Wails 望而却步,现在或许是重新审视它的好时机。

想尝鲜的开发者,现在就可以执行:

go install github.com/wailsapp/wails/v3/cmd/wails3@latest
wails3 setup
wails3 init

如果在使用中遇到可复现的 bug,官方希望你附上 wails3 doctor 的输出和一个最小复现示例;如果你想提议新能力或者公开行为的变更,官方建议直接发起一个 WEP 草案 PR,而不是简单开一个 feature request issue。

参考链接:

(本文相关代码示例基于 Wails v3 官方文档整理,运行前请确保本机已安装 Go 1.25 及以上版本,并根据所在平台完成 wails3 setup 环境检查。)


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。