运行时概览
本教程共 42 篇 · 第 16 篇 · 更新于 2026-08-03
16. 运行时概览
本节目标
- 说清楚 Runtime 是什么、和绑定机制是什么关系
- 掌握 Go 运行时的导入路径与 context 参数规则
- 知道前端
window.runtime提供了哪些能力、和 Go 侧差在哪 - 会用
Hide/Show/Quit/Environment这几个应用级方法 - 建立后续章节(对话框、通知、窗口、剪贴板)的整体地图
16-1 Runtime 是什么
前面几章讲的绑定和事件,解决的是「数据怎么在 Go 和前端之间流动」。但桌面应用还需要另一类能力:弹一个系统对话框、把窗口最大化、往剪贴板写点东西、发一条桌面通知。
这些能力浏览器里要么没有,要么受限。Wails 把它们统一封装成一个库,叫 Runtime(运行时)。
官方对它的定义很朴素:「一个为应用程序提供实用方法的库」。它包含这几大块:
| 模块 | 能做什么 | 对应章节 |
|---|---|---|
| Window | 窗口的显示、隐藏、尺寸、位置、全屏 | 第 19、20 章 |
| Menu | 运行时更新应用菜单 | 第 22、23 章 |
| Dialog | 消息框、文件选择、保存对话框 | 第 17 章 |
| Events | Go ↔ 前端双向事件 | 第 14 章 |
| Notification | 系统级桌面通知 | 第 18 章 |
| Browser | 用系统默认浏览器打开链接 | 第 27 章 |
| Screen | 获取屏幕分辨率、缩放信息 | 第 27 章 |
| Clipboard | 读写系统剪贴板 | 第 26 章 |
| Log | 分级日志输出 | 本章 16-5 |
| DragAndDrop | 接收拖入窗口的文件 | 第 25 章 |
一句话概括三者的分工:
- 绑定:前端主动喊 Go,要一个结果
- 事件:任意一方广播消息,另一方订阅
- 运行时:调用宿主系统的原生能力
16-2 Go 侧怎么用
导入路径是固定的:
import "github.com/wailsapp/wails/v2/pkg/runtime"
Warning这个包名和 Go 标准库的
runtime撞了。如果同一个文件里既要用runtime.GOOS又要用 Wails 的运行时,必须给其中一个起别名,比如import wruntime "github.com/wailsapp/wails/v2/pkg/runtime"。这是新手最容易被编译器骂的地方之一。
所有 Go 运行时方法的第一个参数都是 context.Context。 没有例外。
这个 context 不能自己造,必须来自 Wails 的生命周期回调——OnStartup 或 OnDomReady。标准做法是存进结构体:
package main
import (
"context"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
type App struct {
ctx context.Context
}
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
func (a *App) SayHello() {
runtime.LogInfo(a.ctx, "有人点了按钮")
}
Warning
OnStartup里虽然能拿到 context,但此时窗口还在另一个线程初始化,不保证运行时方法能正常工作。想在启动阶段调运行时(发事件、弹对话框、改窗口标题),请放到OnDomReady里。这是官方文档明确标注的注意事项。
传错 context 的后果通常是 panic 或者静默失败。养成习惯:只用 Wails 给的那个,不要用 context.Background() 顶替。
16-3 前端侧怎么用
JavaScript 运行时挂在全局对象 window.runtime 上:
window.runtime.LogPrint("来自前端的日志");
window.runtime.WindowSetTitle("新标题");
window.runtime.BrowserOpenURL("https://wails.io");
它和 window.go 一样,由 Wails 注入的 /wails/runtime.js 提供。
跑过一次 wails dev 之后,frontend/wailsjs/runtime/ 目录下会生成带 TypeScript 声明的模块,可以按需 import:
import { EventsOn, WindowMinimise, ClipboardSetText } from "../wailsjs/runtime/runtime";
function handleCopy(text: string) {
ClipboardSetText(text);
}
推荐用 import 的方式,理由和第 15 章一样——有类型提示。window.runtime 留给控制台调试。
Go 和 JS 两套运行时不完全对等。 官方的目标是「尽量保持一致」,但受平台能力限制有几处明显差异:
| 能力 | Go 运行时 | JS 运行时 |
|---|---|---|
| Events | 支持 | 支持 |
| Window | 支持 | 支持 |
| Dialog | 支持 | 不支持 |
| Notification | 支持 | 部分支持(无 OnNotificationResponse) |
| Clipboard | 支持 | 支持 |
| Log | 支持 | 支持 |
Note对话框只能在 Go 侧调用。前端想弹一个文件选择框,得先绑定一个 Go 方法,在方法里调
runtime.OpenFileDialog,再把结果返回给前端。第 17 章会给出完整写法。
16-4 四个应用级方法
Runtime 里有几个不属于任何子模块的顶层方法,直接作用于整个应用。
Quit —— 退出应用
Go: Quit(ctx context.Context)
JS: Quit()
func (a *App) ExitApp() {
runtime.Quit(a.ctx)
}
调用它会走完整的退出流程,包括触发 OnBeforeClose 和 OnShutdown。所以如果你在 OnBeforeClose 里返回了 true(阻止关闭),Quit 也退不掉。
Hide / Show —— 隐藏与显示应用
Go: Hide(ctx context.Context) / Show(ctx context.Context)
JS: Hide() / Show()
这两个方法有平台差异:
在 macOS 上,Hide 的行为等同于系统菜单里的「隐藏 xxx」,应用从前台消失但进程还在,可以用 Cmd+Tab 切回来。Show 则把应用重新带到前台。
在 Windows 和 Linux 上,它们目前的行为和 WindowHide / WindowShow 一致,也就是只操作窗口本身。
Tip做托盘常驻应用(第 24 章)时,点关闭按钮不退出而是
Hide,是很常见的交互设计。配合OnBeforeClose返回true拦截退出,再调Hide藏起来。
Environment —— 获取运行环境信息
Go: Environment(ctx context.Context) EnvironmentInfo
JS: Environment(): Promise<EnvironmentInfo>
返回的结构体:
type EnvironmentInfo struct {
BuildType string // "dev" 或 "production"
Platform string // "windows" / "darwin" / "linux"
Arch string // "amd64" / "arm64" 等
}
前端对应的接口:
interface EnvironmentInfo {
buildType: string;
platform: string;
arch: string;
}
典型用法是按平台调整界面。macOS 的窗口控制按钮在左上角,Windows 在右上角,做无边框窗口时就得靠这个判断:
import { useEffect, useState } from "react";
import { Environment } from "../wailsjs/runtime/runtime";
export default function TitleBar() {
const [platform, setPlatform] = useState("");
useEffect(() => {
Environment().then((info) => setPlatform(info.platform));
}, []);
return platform === "darwin" ? <MacControls /> : <WinControls />;
}
BuildType 则可以用来区分开发和生产环境,比如只在 dev 下显示调试面板。
16-5 日志:一个被低估的模块
Runtime 里的 Log 模块提供了六个级别:Trace、Debug、Info、Warning、Error、Fatal。日志器只输出当前级别及以上的消息。
Go 侧的方法名规律很整齐,每个级别都有普通版和格式化版:
runtime.LogTrace(a.ctx, "详细追踪信息")
runtime.LogDebug(a.ctx, "调试信息")
runtime.LogInfo(a.ctx, "一般信息")
runtime.LogWarning(a.ctx, "警告")
runtime.LogError(a.ctx, "错误")
runtime.LogFatal(a.ctx, "致命错误")
// 带格式化的版本,后缀是 f
runtime.LogInfof(a.ctx, "用户 %s 登录成功", userName)
还有一个 LogPrint / LogPrintf,输出原始消息,不带级别前缀。
前端侧同名调用:
window.runtime.LogInfo("前端记录一条");
window.runtime.LogError("出错了");
好处是前后端日志汇总到同一个输出流,排查跨语言问题时时间线是连贯的。用 console.log 的话,日志只在 devtools 里,跟 Go 的终端输出对不上。
日志级别可以在 options.App 里配,LogLevel 管开发环境,LogLevelProduction 管生产环境,默认分别是 Debug 和 Error。
Tip生产环境默认只输出 Error,这是合理的。但用户报障时你什么信息都拿不到。可以做一个隐藏的调试开关,允许临时把级别调低。
常见误区
自己造 context。用 context.Background() 或 context.TODO() 调运行时方法,轻则不生效,重则 panic。必须用 Wails 通过生命周期回调给你的那个。
包名冲突没起别名。Go 标准库也有 runtime 包。同文件混用必须起别名,否则编译报错,错误信息还挺容易看岔。
在 OnStartup 里调运行时。窗口没初始化完,行为不确定。挪到 OnDomReady。
以为前端能调对话框。window.runtime 里没有 Dialog。这是设计如此,不是 bug。
忘记 Environment 在前端是异步的。Go 侧 Environment(ctx) 直接返回结构体,前端 Environment() 返回 Promise,要 await。
把 Quit 当强制退出用。它会走完整退出流程,OnBeforeClose 返回 true 时退不掉。真要强退得先把拦截逻辑处理掉。
小结
Runtime 是 Wails 通向操作系统的那扇门。Go 侧导入 github.com/wailsapp/wails/v2/pkg/runtime,所有方法第一个参数传 Wails 给的 context;前端侧用 window.runtime 或 import 生成的模块。
两套运行时大部分对等,最大的例外是对话框——只有 Go 能调。
顶层的 Quit/Hide/Show/Environment 加上 Log 模块,是每个项目都会用到的基础件。剩下的子模块从下一章开始逐个展开,第 17 章先讲对话框。