首页 / Wails 入门教程 / 模板、社区与生态

Wails 入门教程

模板、社区与生态

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

Wails桌面开发模板社区开源案例

42. 模板、社区与生态

本节目标

  • 掌握官方内置模板清单和远程模板的安装方式
  • 会用 wails generate template 把自己的前端脚手架变成可复用模板
  • 读懂真实项目 Tiny RDM 的目录组织和它对 Wails 的用法
  • 知道 Angular、SvelteKit 这类框架接入时要额外改什么
  • 找到官方社区入口,遇到问题知道去哪问

42-1 官方内置模板清单

前面所有章节都用 react-ts 这一个模板。实际上 Wails v2.13.0 内置了十几个,随时可以列出来:

wails init -l

按框架分组大概是这样:

  • Reactreactreact-ts
  • Vuevuevue-ts
  • Sveltesveltesvelte-ts
  • Preactpreactpreact-ts
  • Litlitlit-ts
  • 原生vanillavanilla-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.jsonwails.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.jsonpackage-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: trueOnDomReady 的配合:窗口先不显示,等前端 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 !webmain_web.go//go:build web。服务层也成对出现 platform_desktop.goplatform_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.jsonfrontend:* 命令对不对、有没有整页跳转导致运行时丢失。

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 把它做出来。工具的价值在用起来之后才显现。

上一篇
移动端概览 Mobile
下一篇
已经是最后一篇啦