调试与排错
本教程共 42 篇 · 第 39 篇 · 更新于 2026-08-03
39. 调试与排错
本节目标
- 学会打开 Wails 的三条调试通道:dev 控制台、浏览器 DevTools、生产包 devtools
- 能按固定顺序排查白屏、404、绑定失效这些高频故障
- 知道 Windows / macOS / Linux 上各自容易踩的环境坑怎么绕开
- 一眼认出项目里混进来的 Wails v1 旧写法,并知道对应的 v2 替代品
39-1 先把调试通道打开
排错的前提是能看见东西。Wails 一共给了三个观察窗口,很多人只用了第一个。
第一个是 wails dev 的终端输出。它会打印 Go 编译错误、绑定生成过程、资源变更触发的重载。编译失败时它不会退出,而是保留上一个能跑的版本继续运行,红色报错停在终端里等你看。
第二个是浏览器。wails dev 启动时会额外开一个 HTTP 服务在 http://localhost:34115,把整个应用(不只是前端)通过 http 暴露出来。用 Chrome 打开这个地址,就能用你熟悉的 React DevTools、网络面板、断点调试器。
# 启动开发模式,并自动打开浏览器
wails dev -browser
# 把日志级别调到 Trace,看清每一步做了什么
wails dev -loglevel Trace
# 检测到资源变化后等 500ms 再重载,避免保存一次触发多轮刷新(默认 100 毫秒)
wails dev -debounce 500
NoteWails CLI 的参数是单横线风格,写成
-loglevel而不是--log-level。网上不少旧文用双横线,抄过去会直接报参数错误。
第三个是应用窗口内的 DevTools。开发模式下窗口里可以直接右键唤出检查器;打包之后默认是关掉的。想在正式包里临时留一个口子,构建时加参数:
# 保留调试信息 + 调试控制台 + 窗口内 devtools
wails build -debug
# 不带 debug 信息,但允许 Ctrl/Cmd+Shift+F12 打开 devtools
wails build -devtools
Warning
-devtools打出来的包无法通过 Mac App Store 审核,只在自己排查问题时用,发布前记得去掉。
还可以让调试版启动时自动弹出检查器,写在 options.App 里:
err := wails.Run(&options.App{
Title: "myapp",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Debug: options.Debug{
OpenInspectorOnStartup: true,
},
Bind: []interface{}{app},
})
这个字段只在调试构建里生效,正式包不受影响,可以放心留着。
39-2 白屏:按四步查
白屏是新手遇到最多的问题,窗口起来了,里面一片空白。别猜,按顺序走。
第一步,看 embed 路径。 main.go 里那行注释是真代码,删了或改错就没资源:
//go:embed all:frontend/dist
var assets embed.FS
第二步,看 frontend/dist 目录里到底有没有东西。 用 wails dev 时前端由开发服务器提供,dist 是空的也能跑;一旦 wails build,空 dist 就直接白屏或者 404。手动补一次前端构建:
cd frontend && npm install && npm run build
第三步,看前端路由。 SPA 用了 history 模式,打包后访问非根路径会拿不到文件。桌面端优先用 hash 路由,这一点第 10 章讲过。
第四步,macOS 上还要看 Info.plist。 有一类白屏只在 Mac 出现,是本地网络访问被拦了。往 build/darwin/Info.plist 里加:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
Tip「
wails dev一切正常,wails build之后 404」几乎可以断定是前端产物或路由问题,不用去怀疑 Go 代码。
顺带说一个 Mac 上的观感问题:应用启动瞬间闪一下白。那是 WebView 的默认白底,把它设成透明就好:
Mac: &mac.Options{
WebviewIsTransparent: true,
},
39-3 装不上、跑不起来的环境坑
wails 命令找不到。 说明 Go 的 ~/go/bin(Windows 是 %USERPROFILE%\go\bin)没进 PATH。改完环境变量要关掉所有已开的终端重开,老窗口读的是旧环境。
下载 Wails 卡住或超时。 国内访问官方 Go Proxy 经常失败,换成国内镜像:
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
装了 WebView2,wails doctor 还说没装。 大概率是运行时架构装错了,比如 64 位系统装了 x86 版本。去微软官网重新下载对应架构的 Evergreen 运行时。
Node 版本对不上。 报错长这样:Cannot start service: Host version "x.x.x" does not match binary version "x.x.x"。原因是 node_modules 是在另一台 Node 版本不同的机器上装的。删掉重装:
cd frontend
rm -rf node_modules package-lock.json
npm install
Note建议把
frontend/node_modules和frontend/package-lock.json加进.gitignore,从根上避免跨机器版本打架。
遇事先跑一次 wails doctor,它会把操作系统版本、Go 版本、CPU 架构,以及 WebView2、npm 这些必需依赖和 upx、nsis 这些可选依赖挨个列出来,最后给一句「你的系统是否已准备好」的结论,比自己一个个试快得多。
39-4 绑定与调用类问题
构建卡在 Generating bindings 不动。 Wails 生成绑定的方式是用特殊模式跑一遍你的程序,读出被绑定的类型。如果代码里有死循环,或者 wails.Run() 返回后还有 goroutine 挂着不退出,这一步就永远结束不了。检查 main 能不能正常退出。
变参方法调用失败。 Go 侧定义成变参:
func (a *App) TestFunc(msg string, args ...interface{}) error {
// ...
return nil
}
前端用展开语法传会失败:
// 这样调用会失败
window.go.main.App.TestFunc(msg, ...args);
去掉三个点,整个数组传过去:
const msg = "Hello ";
const args = ["Go", "JS"];
window.go.main.App.TestFunc(msg, args)
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
生成的 TypeScript 类型不准。 某些 Go 类型映射到 TS 不理想,可以用结构体标签指定:
type User struct {
ID int `json:"id"`
CreatedAt string `json:"createdAt" ts_type:"Date | string"`
}
离开 index.html 后调不动 Go 方法。 如果你用 <a href> 或 window.location 跳到另一个 html 文件,Wails 注入的上下文就丢了。要么改用前端路由不做整页跳转,要么在新页面的 <head> 里手动补上运行时:
<head>
<script src="/wails/ipc.js"></script>
<script src="/wails/runtime.js"></script>
</head>
复杂结构体传到前端变形。 这是老项目里常见的抱怨。v2 已经能自动把 Go 结构体转成 TypeScript,多数情况没问题;真遇到嵌套过深或者含接口字段的对象,最省事的办法是 Go 侧直接 json.Marshal 成字符串返回,前端再 JSON.parse,中间少一层不确定性。
39-5 日志放在哪里看
Go 侧不要用 fmt.Println 打日志,用运行时的日志 API,它会统一走 Wails 的日志器,级别可控:
import "github.com/wailsapp/wails/v2/pkg/runtime"
func (a *App) DoWork() error {
runtime.LogInfo(a.ctx, "开始处理任务")
if err := a.doSomething(); err != nil {
runtime.LogErrorf(a.ctx, "处理失败: %v", err)
return err
}
return nil
}
Print、Trace、Debug、Info、Warning、Error、Fatal 七档各有一对函数:不带 f 的只接一个字符串,带 f 的是格式化版本(LogInfof、LogErrorf 等),用法和 fmt.Printf 一样。想在运行期临时改级别,用 runtime.LogSetLogLevel(ctx, logger.TRACE)。
日志级别在 options.App 里分开配置,开发和生产用不同档位:
import "github.com/wailsapp/wails/v2/pkg/logger"
err := wails.Run(&options.App{
LogLevel: logger.DEBUG, // 开发模式,默认 Info
LogLevelProduction: logger.ERROR, // 打包后,默认 Error
// ...
})
前端的 console.log 在 wails dev 时会出现在窗口的 DevTools 控制台里,也会出现在 http://localhost:34115 那个浏览器标签页里。两边看到的是同一个应用,选顺手的那个。
39-6 各平台的零散坑
macOS:too many open files。 系统默认单进程只能开 256 个文件句柄,wails dev 的文件监听很容易撑爆。临时抬高上限:
ulimit -n 1024
macOS:奇怪的编译错误。 报错指向 Foundation.framework 里的头文件,通常是系统版本和 Xcode 命令行工具版本对不上。先升级命令行工具;如果还不行,检查工具链路径:
xcode-select -p
# 若输出 /Applications/Xcode.app/Contents/Developer,切回命令行工具
sudo xcode-select --switch /Library/Developer/CommandLineTools
Apple Silicon 上还可能碰到链接期报 _OBJC_CLASS_$_UTType 未定义,补一个链接参数:
export CGO_LDFLAGS="-framework UniformTypeIdentifiers"
Windows:图标不刷新。 换了 build/windows/icon.ico 之后资源管理器还显示旧图标,是系统图标缓存没更新。删掉 C:\Users\<用户名>\AppData\Local 下隐藏的 IconCache.db,重启资源管理器。
Windows:管理员权限下报 “Microsoft Edge can’t read or write to its data directory”。 场景是普通用户通过 UAC 输入管理员账号启动你的程序,WebView2 的子进程仍跑在普通用户身份下,访问不了管理员的 %APPDATA%。最稳的解法是重构成不常驻管理员权限,只在需要时用 runas 单独提权跑那一小段任务。实在必须全程管理员,就把 WebView2 数据目录换到两个账号都能读写的位置:
Windows: &windows.Options{
WebviewUserDataPath: "C:\\ProgramData\\Sample",
},
39-7 识别混进来的 v1 旧代码
Wails v1 和 v2 的 API 差别非常大,网上大量中文文章还停留在 v1。如果你复制的代码报「未定义」「参数类型不匹配」,先确认它是不是 v1 的。
Warning这是 v1 旧写法,不要这样做。以下几种签名在 v2.13.0 中已全部移除,看到就换掉。
// v1:应用配置结构体名字不同 app := wails.CreateApp(&wails.AppConfig{Width: 1024, Height: 768}) app.Bind(basic) app.Run()// v1:前端通过 backend 命名空间调用 window.backend.App.Greet("world");v1 还有
wails.Init(...)、wails.BuildMenu(...)、wails.MenuItem{...}、wails.SetSystemTray(...)、runtime.Run(...)这些入口,在 v2.13.0 的写法里一个都用不上,照抄过来只会编译不过。
对应到 v2.13.0,正确的写法是这样:
err := wails.Run(&options.App{
Title: "myapp",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Bind: []interface{}{app},
})
// v2:前端走 go 命名空间,或者直接 import 生成的绑定
import { Greet } from "../wailsjs/go/main/App";
Greet("world").then((res) => console.log(res));
菜单从 wails.BuildMenu 换成 menu.NewMenu(),托盘不再由框架内置。判断依据也很简单:导入路径里带 wails/v2 的才是本教程的基线。
常见误区
以为白屏一定是 Go 的问题。 绝大多数白屏出在前端产物和资源路径上,先查 dist 再查后端。
在生产包里长期开着 devtools。 方便是方便,但等于把内部实现摊开给用户,也过不了 Mac 商店审核。
把 wails dev 的表现当成打包后的表现。 dev 模式资源从磁盘读、走开发服务器,生产模式资源在二进制里、走 assetserver。路径相关的 bug 只在打包后才现形,功能做完至少 wails build 验一次。
照抄第三方文章的 CLI 参数。 双横线写法(--loglevel、--platform)多半来自旧文或别的工具,Wails v2.13.0 的 CLI 一律单横线。拿不准就跑一次 wails <命令> -help,以它的输出为准。
小结
排错的效率取决于你能看到多少信息。先用 wails dev -loglevel Trace 加浏览器 DevTools 把现场铺开,再按「资源 → 绑定 → 平台环境」的顺序缩小范围。遇到看不懂的 API,先确认它属于 v1 还是 v2,这一步能省掉一半无谓的挣扎。下一章把这些手动操作搬进 IDE,让断点直接落在 Go 代码上。