模板、社区与生态
本教程共 42 篇 · 第 42 篇 · 更新于 2026-08-03
42. 模板、社区与生态
本节目标
- 掌握官方内置模板清单和远程模板的安装方式
- 会用
wails generate template把自己的前端脚手架变成可复用模板 - 读懂真实项目 Tiny RDM 的目录组织和它对 Wails 的用法
- 知道 Angular、SvelteKit 这类框架接入时要额外改什么
- 找到官方社区入口,遇到问题知道去哪问
42-1 官方内置模板清单
前面所有章节都用 react-ts 这一个模板。实际上 Wails v2.13.0 内置了十几个,随时可以列出来:
wails init -l
按框架分组大概是这样:
- React:
react、react-ts - Vue:
vue、vue-ts - Svelte:
svelte、svelte-ts - Preact:
preact、preact-ts - Lit:
lit、lit-ts - 原生:
vanilla、vanilla-ts - 空壳:
plain
命名规律很清楚,带 -ts 后缀的是 TypeScript 版本,不带的是 JavaScript 版本。除了 plain,其余模板都预置了 Vite,开发时的热重载靠它。
plain 值得单独说一句:它只给一个最小的 HTML 骨架,没有任何前端构建工具。适合两种场景——做纯静态界面的小工具,或者你打算自己从零接一套构建链。
# 用 Vue + TypeScript 起一个项目
wails init -n myapp -t vue-ts
# 用最小骨架,前端自己配
wails init -n myapp -t plain
Tip模板只影响初始文件,不锁定后续技术栈。用
react-ts建的项目照样能把前端换成别的,只要产物最终落到frontend/dist(或者你在main.go里 embed 的那个目录)就行。
42-2 远程模板与社区模板
内置模板覆盖不了所有搭配。想要 React + TailwindCSS + shadcn/ui 这种组合,或者 Next.js、Quasar、Solid、HTMX,就得用远程模板。
-t 参数除了接模板名,也能直接接 GitHub 仓库地址:
# 使用主分支
wails init -n myapp -t https://github.com/misitebao/wails-template-vue
# 锁定某个 tag 版本
wails init -n myapp -t https://github.com/leaanthony/testtemplate@v1.0.0
不带版本后缀就取主分支,带了就取对应 tag 的代码。生产项目建议锁版本,避免模板作者一次提交把你的构建搞挂。
官方文档的社区模板页维护了一份清单,涵盖 Vue、Angular、React、Svelte、Solid、Elm、HTMX、Lit 等生态,数量还在增加。
Warning社区模板由第三方维护,Wails 官方不负责也不背书。用之前打开
package.json和wails.json看一眼:装了哪些包、frontend:build跑的是什么命令。这是最低限度的自查。
42-3 做一个自己的模板
团队里如果反复用同一套技术栈起项目,把它固化成模板比每次手动搭快得多。
生成一个空白模板骨架:
wails generate template -name mytemplate
会得到这样一个目录:
mytemplate/
├── NEXTSTEPS.md # 完成模板的后续步骤说明
├── README.md # 模板发布时的说明文档
├── app.tmpl.go # app.go 的模板文件
├── frontend/
│ └── dist/ # 前端资源目录
├── go.mod.tmpl # go.mod 的模板文件
├── main.tmpl.go # main.go 的模板文件
├── template.json # 模板元信息
└── wails.tmpl.json # wails.json 的模板文件
.tmpl 后缀的文件会在 wails init 时做变量替换,把项目名、模块名填进去。template.json 存模板的名称、描述、helpurl 这些元信息。
更常见的做法是从已有前端项目转模板。比如你已经用 Vite 搭好了一套 React + Tailwind 的脚手架,直接指过去:
wails generate template -name wails-template-my-react -frontend ./my-react-base/
CLI 会把现有项目挪进 frontend 目录,同时把 package.json、package-lock.json 改名成 .tmpl.json 并注入变量。之后按 NEXTSTEPS.md 补完剩余配置。
本地测试:
wails init -n my-test-project -t ./wails-template-my-react/
cd my-test-project
wails build
发布就是推到 GitHub。推之前把 .git 之类的无关文件从 frontend 目录里清掉,template.json 填完整。
42-4 真实案例:Tiny RDM 的项目结构
看了这么多模板,不如看一个真跑在用户机器上的项目。Tiny RDM 是一个跨平台的 Redis 桌面客户端,用 Wails v2 + Vue3 写的,代码组织得相当清爽。
顶层结构:
tiny-rdm/
├── main.go # 桌面版入口(构建标签 !web)
├── main_web.go # Web 版入口(构建标签 web)
├── wails.json # Wails 项目配置
├── backend/ # Go 侧全部代码
│ ├── services/ # 业务服务,绑定给前端的就是这些
│ ├── storage/ # 本地存储:连接配置、偏好设置
│ ├── types/ # 前后端共享的数据结构
│ ├── consts/ # 常量
│ ├── utils/ # 工具函数
│ └── api/ # Web 版专用的 HTTP/WebSocket 层
├── frontend/ # Vue3 前端
└── build/ # 各平台打包资源
├── appicon.png
├── darwin/ # Info.plist / Info.dev.plist
├── windows/ # icon.ico / installer / manifest
└── linux/
第一个可借鉴的点:Go 代码全部收在 backend/ 下,按职责分包。 根目录只留入口文件。这比把几十个 .go 文件平铺在根目录清楚太多。
第二个可借鉴的点:绑定的是「服务」而不是一个巨型 App 结构体。 看它的 wails.Run 调用:
err := wails.Run(&options.App{
Title: appName,
Width: windowWidth,
Height: windowHeight,
MinWidth: consts.MIN_WINDOW_WIDTH,
MinHeight: consts.MIN_WINDOW_HEIGHT,
WindowStartState: windowStartState,
Frameless: !isMacOS,
Menu: appMenu,
EnableDefaultContextMenu: true,
AssetServer: &assetserver.Options{
Assets: assets,
},
StartHidden: true,
Bind: []interface{}{
sysSvc, connSvc, browserSvc, cliSvc, monitorSvc, pubsubSvc, prefSvc,
},
})
七个服务分别绑定,生成的前端绑定按服务分文件,导入时命名空间天然分开:
import { Info } from "wailsjs/go/services/systemService";
import { SelectFile } from "wailsjs/go/services/systemService";
功能一多,这种切法比单个 App 结构体挂几十个方法好维护。
第三个可借鉴的点:生命周期钩子各司其职。
OnStartup: func(ctx context.Context) {
sysSvc.Start(ctx)
connSvc.Start(ctx)
// ... 其余服务依次启动
},
OnDomReady: func(ctx context.Context) {
x, y := prefSvc.GetWindowPosition(ctx)
runtime.WindowSetPosition(ctx, x, y)
runtime.WindowShow(ctx)
},
OnBeforeClose: func(ctx context.Context) (prevent bool) {
x, y := runtime.WindowGetPosition(ctx)
prefSvc.SaveWindowPosition(x, y)
return false
},
OnShutdown: func(ctx context.Context) {
browserSvc.Stop()
cliSvc.CloseAll()
},
注意 StartHidden: true 和 OnDomReady 的配合:窗口先不显示,等前端 DOM 就绪、位置恢复完毕再 WindowShow。这样用户看不到窗口从默认位置跳到记忆位置的过程,启动观感干净很多。这个小技巧很值得抄。
第四个可借鉴的点:平台差异用独立的 Options 块处理。
Mac: &mac.Options{
TitleBar: mac.TitleBarHiddenInset(),
About: &mac.AboutInfo{ /* ... */ },
},
Windows: &windows.Options{
DisableFramelessWindowDecorations: false,
},
Linux: &linux.Options{
ProgramName: appName,
Icon: icon,
WebviewGpuPolicy: linux.WebviewGpuPolicyOnDemand,
},
配合 Frameless: !isMacOS —— Mac 用系统的隐藏式标题栏,Windows 和 Linux 用无边框自绘。同一份代码在三个平台上都符合各自的习惯。
第五个可借鉴的点:用构建标签支持两种形态。 main.go 顶部是 //go:build !web,main_web.go 是 //go:build web。服务层也成对出现 platform_desktop.go 和 platform_web.go,前者包装 Wails runtime,后者走 WebSocket:
//go:build !web
package services
import "github.com/wailsapp/wails/v2/pkg/runtime"
type OpenDialogOptions = runtime.OpenDialogOptions
func EventsEmit(ctx context.Context, event string, data ...any) {
runtime.EventsEmit(ctx, event, data...)
}
业务代码调的是 services.EventsEmit,不直接依赖 Wails。这正是上一章说的分层思路,在真实项目里的落地样子。
最后一个细节:统一的返回结构。 所有绑定方法返回同一个类型:
type JSResp struct {
Success bool `json:"success"`
Msg string `json:"msg"`
Data any `json:"data,omitempty"`
}
前端拿到的永远是 { success, msg, data },错误处理只写一次。相比每个方法各自返回 (T, error),前端的样板代码少很多。
Note建议自己把这个仓库 clone 下来跑一遍
wails dev,边点边对着代码看。读一个跑得起来的真实项目,比读十篇教程收获大。
42-5 其他前端框架接入的差异
主线是 React,但如果团队用别的框架,有几个地方要额外处理。
Angular 没有官方模板,但能接。改 wails.json 里的四个字段:
{
"frontend:install": "npm install",
"frontend:build": "npx ng build",
"frontend:dev:watcher": "npx ng serve",
"frontend:dev:serverUrl": "http://localhost:4200"
}
然后在 index.html 的 <head> 里关掉自动注入、手动引入运行时:
<head>
<meta name="wails-options" content="noautoinject" />
<script src="/wails/ipc.js"></script>
<script src="/wails/runtime.js"></script>
</head>
SvelteKit 的坑集中在两点。一是所有带 server 字样的文件(+page.server.ts、+server.ts)都会构建失败,因为桌面端全部路由都要预渲染。二是整页跳转会把 Wails 运行时卸载掉,之后调不动任何 Go 方法。解决办法是用 goto() 做路由跳转,避免整页刷新;实在避不开就照 Angular 那样在 app.html 里手动引入运行时。
配置上还要把适配器换成 @sveltejs/adapter-static,开启 SPA 模式:
// frontend/src/routes/+layout.ts
export const prerender = true;
export const ssr = false;
同时把 main.go 里的 embed 路径从 frontend/dist 改成 frontend/build,因为 SvelteKit 的产物目录名不一样。
Tip这条经验适用于所有框架:换前端方案时,先确认三件事——产物目录叫什么、
wails.json的frontend:*命令对不对、有没有整页跳转导致运行时丢失。
42-6 社区与求助路径
遇到卡住的问题,按这个顺序找:
- awesome-wails:社区维护的资源汇总,模板、库、开源应用都在里面
- GitHub Issues:先搜再提,多数常见问题已经有人问过并给了解法
- 官方 Discord:适合问「这样设计对不对」这类没有标准答案的问题
- Wails 中文 QQ 群:群号 1067173054,中文语境下沟通更快
提问的时候把 wails doctor 的完整输出贴上,能省掉一半来回确认的时间。
常见误区
认为模板决定了项目上限。 模板只是起点,后面想加什么加什么,改 wails.json 就行。
直接用来路不明的社区模板。 至少看一眼它的构建脚本和依赖列表,别让别人的 postinstall 在你机器上随便跑。
照抄开源项目的每一行。 Tiny RDM 的双形态构建、服务分层是它自己的需求驱动出来的。小工具用一个 App 结构体挂几个方法完全够用,过早抽象反而拖慢进度。
在中文社区搜到什么用什么。 大量文章还停在 v1,认准 wails/v2 的导入路径,或者直接对照官方文档确认。
小结
模板系统让 Wails 的起步成本很低:内置十几套常见组合,远程模板打开了整个社区生态,wails generate template 让团队能沉淀自己的脚手架。真正提升水平的方式是读代码——Tiny RDM 展示了服务分层、生命周期编排、平台差异隔离、统一返回结构这些在真实产品里被验证过的做法。
整套教程到这里就结束了。从环境准备、第一个应用,到绑定与事件、窗口与系统能力、打包分发、调试排错,四十二章覆盖的是知识点而不是某一个具体项目。接下来该动手了:挑一个自己每天都会用到的小需求,用 Wails 把它做出来。工具的价值在用起来之后才显现。