作品 / Wails 桌面端脚手架基座
开发基座 · DEVTOOLS2026NO.15开源项目

Wails 桌面端脚手架基座

基于 Wails v2 的桌面客户端脚手架基座——Go + Vue3,内置运行时能力、管道契约与架构护栏

GoGORMSQLiteWailsVue3Element PlusVitePinia
Wails 桌面端脚手架基座 界面截图
1 / 2
仪表盘 · 应用壳

缘起

第一个桌面客户端发出去,收到的反馈是「打不开」,而我这边跑得好好的。想定位就得知道它崩在哪一步,可应用连一行日志都没往盘上落。那之后我给自己定了条规矩:任何桌面项目,先让它在别人的机器上说得清自己死在哪。

背景

桌面客户端项目每次起盘,操作系统这一层都要重新打交道:窗口生命周期、单实例锁、系统托盘、日志轮转、配置读写、崩溃捕获、 打包安装。这些与业务无关,但每一件都要做一次决定,而每次决定都可能和上一个项目不一致。需要一套与业务 无关、可反复复用的桌面基座,把这些"管道"沉淀下来,作为后续所有桌面项目的起点。

方案

基座的定位是「只给管道,产品面下游自己长」——它提供运行时能力与契约,不预置业务页面:

  • 运行时能力:单实例锁(二次启动唤起已有窗口)、系统托盘 + 右键菜单(Windows/Linux)、 日志轮转(5MB × 3 份)、config.json 读写、原生对话框、panic 落盘、优雅关闭
  • 管道契约(Go 与 TS 两端单一真相在 Go):
    • 错误协议 internal/apperr + Wails ErrorFormatter → 前端拿到 {code, message}
    • 分页协议 internal/page 的 Request / Result[T]
    • 事件协议 internal/event 的 <domain>:<action> 命名约定
  • 架构护栏:把约定编译成"会红的检查"——分层越界、模型注册双向校验、依赖登记制 (deps.yaml 双向校验)、前端 import 安全与硬编码色值,随 make test / make lint 执行; 护栏"感知自己瞎了":解析到 0 个结果会 Fatal 而非静默放行
  • 模块生成器:make gen name=<模块> 从黄金范例 _example/ 生成 model + service + 绑定 + 页面 + 路由 + 菜单,锚点注入且幂等
  • 打包发布:make package 产出 macOS 美化 dmg(Applications 软链 + 背景图合成 + 箭头 同源对齐 + 回读校验)或 Windows NSIS 安装器(scope=user|machine)

技术架构

模块技术选型
后端Go ≥ 1.25 + gorm + SQLite(纯 Go 驱动,交叉编译无 CGO)
桌面运行时Wails v2(WebView2 / WKWebView)
前端Vue3 + Vite + Element Plus + Vue Router(hash 模式适配 file://)+ Pinia
设计系统暗色优先,单主色派生全色阶(tokens.css)
工程化Makefile 统一入口 + 架构护栏(AST / ESLint)+ 依赖登记制 + OpenSpec 治理

采用五层分层(L1 桌面运行时 → L2 前端渲染 → L3 绑定方法 app.go → L4 业务服务 → L5 数据存储),app.go 不直接 import gorm/database,业务一律经 service,由护栏强制约束。

成果

形成了可反复复用的桌面客户端母版:scripts/init.sh myapp 一键实例化(替换模块名占位符 __APP_NAME__ 并清空归档历史),make gen 生成业务模块骨架,make test/lint/smoke 靠护栏 与冒烟证明"没改坏"。macOS 产出带拖拽引导的美化 dmg,Windows 产出免管理员的 NSIS 安装器, 目标机器无需 Go / Node.js 运行时。

已知限制

  • macOS 无系统托盘:Wails v2 的 NSApplication delegate 与所有 systray 库冲突,故 macOS 关闭即退出,托盘能力仅在 Windows/Linux 提供
  • 无 headless 模式:冒烟测试定位为本地验证,CI 只编译不启动 GUI
  • 母版防呆:含 __APP_NAME__ 占位符时拒绝打包,需先执行 scripts/init.sh 实例化

有类似的事要管?

说说现在是怎么做的——Excel、微信群、纸质单子都行。我判断能不能做、大概怎么做。

聊聊你的需求