首页 / Wails 入门教程 / 运行时概览

Wails 入门教程

运行时概览

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

Wails桌面开发Runtime运行时context

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 章
EventsGo ↔ 前端双向事件第 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 的生命周期回调——OnStartupOnDomReady。标准做法是存进结构体:

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)
}

调用它会走完整的退出流程,包括触发 OnBeforeCloseOnShutdown。所以如果你在 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 章先讲对话框。