图 1 同一个程序里,左半边可以是网页,右半边可以是框架自己画出来的界面。

10 月 3 日,做 tsup 和 ChatWise 的 EGOIST 在 X 上丢出一句话:MyGo 是一个纯 Go 的桌面框架,窗口可以用系统 webview,也可以用它自己画的原生界面,“像 GPUI 和 Tauri 生了个孩子,但用 Go 写,所以编译快”。

仓库公开两天,星标冲过 500,协议是 MIT,版本停在 0.2.7。它还很新。值得写,不是因为它已经能替换谁,而是它把近十年桌面应用的几条路线,收成了一个人自己用得动的形状。

先看它要解决什么#

Electron 把整份 Chromium 捆进安装包。能做的事最多,体积和内存也最重。Tauri 把浏览器换成系统自带的 webview,后端改用 Rust,安装包掉到几兆到十几兆。Wails 走同一条路,后端换成 Go,Go 团队不用再学一套所有权系统。

EGOIST 三条都走过。2023 年他发过一张图:同一个应用,Tauri 和 Electron 各写了一遍。今年 9 月他还说,宁可 Rust,也不要 Electron。问题出在下一段:Tauri 和 GPUI 的界面能力他认,Rust 的编译时间和磁盘占用把他自己的节奏拖慢了。发布当天他补了一句,从 Rust 换到 Go 之后,可以随便开 git worktree。

图 2 从左到右:捆浏览器的重窗口、Rust 后端的瘦窗口、Go 加 webview、以及把网页和自绘界面拆开的第四种。

“Rust 编译慢,还占很多磁盘。我用 Tauri 和 GPUI 做过很多应用。Go 的内存效率确实不如 Rust,但在桌面应用上,这个差别没那么大。”

所以 MyGo 不是为了填一个”Go 版 Tauri”的生态空位。顺序是反过来的:人先在做 ChatWise、kero、waku、lorca 这些桌面产品,工具跟不上,才把工具抽成框架。他在 9 月先放了一个叫 QuickGUI 的 GPU 界面实验,有人问差异点,他的回答最接近动机:组件、打包、自动更新可以做成一整套,但唯一的原因是想用 Go 或 TypeScript 快速迭代。发布 MyGo 时他把两者切开:QuickGUI 是实验,MyGo 是拿去生产的。lorca 的 Windows 和 Linux 端已经在用它的 webview,并准备迁到原生界面。

一个窗口,两条内容路径#

MyGo 的窗口壳是共用的:标题、菜单、托盘、对话框、通知、全局快捷键、深链、文件关联。壳里面的内容二选一,同一个应用可以混用。

第一条是网页。它不打包浏览器,用操作系统已经有的 webview:macOS 的 WKWebView,Linux 的 WebKitGTK,Windows 的 WebView2。前端仍是 HTML、CSS 和 JavaScript,模板默认 Vite 加 TypeScript。页面调用 Go,走的是框架从 Go 代码生成的客户端,类型和注释一起带过去。

第二条没有网页。界面用 Go 写,框架自己在 GPU 上画,不启动 webview 进程。作者的说法是,工具、设置页、小实用程序往往不需要一个浏览器引擎。

图 3 上路是网页经消息调用 Go;下路是界面在 Go 里建好,交给 GPU 画出来。窗口壳只有一个。

官方给出的量级:hello world 大约 7 MB;带 webview 的进程起来,macOS 上大约 62 MB;原生界面窗口空闲大约 44 MB;空闲 CPU 为 0。这些是作者自己的测量,不是第三方基准,读的时候留一点余地。但方向是清楚的:不轮询,工作由原生事件或显式唤醒驱动。

架构上,它在防什么#

仓库大约 93% 是 Go。公开 API 在根包,平台差异被压进一份很小的契约。文档写明,改内部实现之前先读架构说明。这不是礼貌,是因为分层是硬的。

图 4 上是你的程序,中是框架自己的策略层,下是 macOS、Linux、Windows 三份只做翻译的后端。

根包做策略:选项默认值、窗口表、退出顺序、调用路由、菜单、自定义协议、信任判断。它不直接碰 AppKit、GTK 或 Win32。中间那层契约只提供事件循环、建窗口、菜单、对话框和剪贴板,事件再回调回来。三个后端只做翻译。另有一个内存后端,用来在没有图形界面的环境里测核心逻辑。不支持的系统在启动时给出明确错误,而不是悄悄半残。

这条分层换来两件具体的事。第一,新增一个桌面能力有固定清单:先设计可以从任意 goroutine 调用的 API,再给契约加最小机制,然后五个后端都要实现,包括测试用的假后端。第二,原生界面那套绘制代码不反向依赖根包。不用它的应用,不会把 GPU 渲染链进去。

线程模型也写死了。Cocoa 和 GTK 必须由进程启动的那个线程驱动,所以主 goroutine 被锁在主线程上,事件循环从这里进去。对外的方法仍可以从别的 goroutine 调,框架负责把工作投回主线程;如果调用方已经在主线程上等待结果,它会抽原生事件,避免自己把自己卡住。绑定的 Go 方法在单独的 goroutine 里跑,允许阻塞。窗口关闭这种事件则在主线程同步执行,所以监听者可以取消关闭。

不用 Cgo,是取舍不是口号#

整个项目要求关掉 cgo。任意一台机器可以交叉编译出其他平台的程序,不需要先装一套 C 工具链。macOS 和 Linux 在运行时打开系统库,Windows 走系统调用。回调按函数签名在启动时建一次,用窗口编号区分目标,不为每个窗口单独建。Metal 着色器提前编译进库,避开启动时那一百多毫秒。

换来的限制同样具体。macOS 的签名和磁盘映像仍然只能在 Mac 上做。Linux 的全局快捷键在 X11 和 Wayland 上不是同一条路。托盘在 Linux 上只有菜单。半透明材质在 Linux 上被忽略。框架把这些差异写进表里,而不是假装三个系统长一样。

网页怎么安全地叫到 Go#

网页侧的调用不是约定,是运行时强制的。你把一个 Go 值绑进去,导出方法就能被页面调用。方法可以带上下文,页面关掉或导航走,这次调用就取消。返回值最多一个结果加一个错误。需要连续吐数据时,用通道,大约每半兆字节要一次确认,避免页面被冲垮。

线上是带密钥的 JSON。每个窗口有一把启动时生成的随机密钥,没有密钥的消息直接丢掉。来源还必须是应用自己的页面、自定义协议、本地文件,或开发时的本机服务器;导航之后重新计算。不可信的调用不会执行 Go。macOS 上还要求消息来自主框架,挡住 iframe。

类型客户端由反射和源码一起生成。嵌入的结构体会被展平,可选字段保持可选,字节切片变成 base64。参数名和注释从 Go 源文件里抽出来,所以编辑器里能看到你写在方法上的说明。开发时 Go 一改,客户端就重写。

原生界面不是套了一层控件#

原生界面是即时模式:每一帧都是状态的函数,焦点、滚动、编辑和动画按位置或显式的键留在元素上。布局有弹性盒和网格。组件覆盖按钮、表单、列表、表格、树、菜单、富文本和路由,输入法有单独的替换区间。

绘制不经过浏览器。界面先变成一幅场景,再交给 Metal、Direct3D 11 或 OpenGL。没有硬件,或者 GPU 路径失败,就在同一帧换成 CPU 绘制。这套 CPU 路径也是测试用的对照图。文本走系统排版:macOS 的 Core Text,Windows 的 DirectWrite,Linux 的 Pango。无障碍同样是每帧一棵树,后端为每个节点维持系统原生的辅助对象,读屏的操作走和键鼠同一套动作。

这是它和 Wails 真正分开的地方。有人在发布帖下说这不就是 Wails,作者回了两个词:更好的开发体验,加上原生界面。webview 在他这里是选项,不是身份。

现在能不能用#

能用来做自己的小工具,不适合当成已经稳定三年的 ABI。原生界面是 10 月 2 日才合进主干的,版本仍是 0.2。发布后两天,作者用它在 20 分钟里做了一个差异查看器,又测了一次全新构建加 Windows 安装包,21 秒。这证明的是他自己的迭代速度,不是社区已经补齐了边角。

适合现在试的人很具体:已经在写 Go,不想为了一个桌面壳引入 Rust,又希望设置页和工具窗不必付浏览器进程的钱。不适合的也同样具体:需要和 Chromium 完全一致的网页能力,或者需要一份一年内不会动的插件接口。Linux 上的网页窗口仍受系统 WebKitGTK 版本拖累,这是所有系统 webview 方案的共同账单。

  • 开发需要 Go 1.27 或更新。网页项目还要 Bun,原生界面项目只要 Go。

  • macOS 12 及以上没有额外运行时。Windows 的网页窗口需要 WebView2,Windows 11 已自带。Linux 需要 GTK 3,网页窗口还要 WebKitGTK。

  • 打包覆盖 macOS 应用和磁盘映像、Windows 安装包、Linux 可执行文件、桌面项和 deb,并带签名更新和增量包。

它真正留下的问题#

桌面应用这十年的主线,是用网页换开发速度,再用系统 webview 把体积还回去。MyGo 承认这笔交易,但拒绝让它成为唯一交易。需要网页的窗口继续用网页;不需要的窗口,回到 Go 里自己画。编译速度被放在内存极限前面,交叉编译被放在调用系统 API 方便前面。

这是一个作者为了自己的产品抽出来的框架,不是一个委员会设计的平台。它的好处和风险是同一件事:迭代会很快,接口也会动。如果它能活过接下来几个月,值得看的不是星标,而是那条原生界面能不能在三个系统上都保持”打开即用、空闲不转”。在那之前,把它当成一条新路线的样品,比当成下一个默认选项更准确。