首页 / Wails 入门教程 / 调试与排错

Wails 入门教程

调试与排错

本教程共 42 篇 · 第 39 篇 · 更新于 2026-08-03

Wails桌面开发调试排错DevTools

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
Note

Wails 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_modulesfrontend/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 的是格式化版本(LogInfofLogErrorf 等),用法和 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.logwails 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 代码上。