昨天第一次真正上手 Wails 时,那种“这也太自由了吧”的震惊感至今还没消退——
可配置的灵活度、极简的项目结构、前后端天然分离的开发模式、前端(Vue)热更新直接作用于桌面窗口、完全不需要安装浏览器内核、打包结果极轻,总总迹象,让我一下子意识到:
“桌面应用的开发方式,真的要被改写了。”
带着这种好奇和兴奋,今天我忍不住又继续开工,想再体验一次它带来的“高效节奏”。
而最能同时检验界面能力、通信流程和数据管理的项目,莫过于一个聊天应用。
于是我决定:用 Wails 做一个简单的桌面聊天软件,看看它到底能走到什么程度。
结果是:
它不仅好用,而且再次刷新了我对桌面开发的认知。
Wails 正在悄悄改变桌面应用的开发模式。
只需 Go + 前端框架(Vue/React) ,就能做出现代化、多平台、高性能的桌面应用。
今天我用 Wails + Vue3 搭建了一个完整的聊天应用(含聊天室切换、表情/图片发送、本地 SQLite 存储)。
本文从学习的角度深入解析项目结构、前后端通信、数据模型、UI 实现方式,以及为什么 Wails 会成为未来桌面开发的热门框架之一。
文中的wails的安装和项目创建这里不再赘述,首次接触它的可以查看上一篇文章Wails + Vue3 构建现代桌面 UI 框架——基于 Go 的跨平台桌面应用实战(含源码案例)
🧱项目结构:Wails 如何将 Go + 前端“自然融合”?
Wails 项目的目录结构非常清晰:
chat-app/
├─ backend/ # Go 后端
│ ├─ chat.go
│ ├─ chat_ws.go
│ ├─ repository.go
│ ├─ file.go
│ └─ models.go
├─ frontend/ # Vue3 前端
│ └─ src/
│ ├─ views/Chat.vue
│ ├─ components/MessageBubble.vue
│ ├─ store/chat.ts
│ └─ styles/
└─ wails.json # 项目配置
└─ main.go
这结构非常典型(各司其职,清晰明了):
前端用 Vue 展示 UI,后端用 Go 做所有逻辑与数据。
而 Wails 提供了关键能力:
自动生成前端可调用的 Go API(TypeScript SDK) 。
你写好一个 Go 方法:
func (c *ChatService) SendMessage(roomID string, msg string) error {
// store message
}
前端就能直接使用:
import { SendMessage } from '@/wailsjs/go/chat/ChatService'
SendMessage("room1", "Hello")
这是我认为 Wails 最大的亮点之一——
无需写任何桥接代码。
🔌双向通信:Wails 的事件模型比我想象的更强
聊天软件最关键的是“消息实时更新”。
Wails 的做法非常优雅:
后端广播事件:
runtime.EventsEmit(c.ctx, "new_message", msg)
前端监听事件
EventsOn("new_message", (msg: Message) => {
chatStore.addMsg(msg)
})
这比 Electron 的 IPC 简单太多,也比 WebSocket 客户端轮询自然太多。
前端完全事件驱动,极其流畅。
🗄️ SQLite 本地存储:离线聊天记录也能秒加载
项目中我添加了一个本地数据库:
func (r *Repository) SaveMessage(m *Message) error {
_, err := r.db.Exec(`
INSERT INTO messages (room_id, sender, type, content, created_at)
VALUES (?, ?, ?, ?, ?)
`, m.RoomID, m.Sender, m.Type, m.Content, m.CreatedAt)
return err
}
加载历史消息:
func (r *Repository) GetMessages(roomID string) ([]Message, error) {
rows, err := r.db.Query(`
SELECT sender, type, content, created_at
FROM messages WHERE room_id = ?
ORDER BY created_at ASC
`, roomID)
...
}
前端只需调用(这样的调用,前端开发省掉了太多的事,它就像在跟你说你只管业务逻辑,其他的都交给我!这不就是我们追求的吗):
LoadHistory(roomID).then(msgs => store.setHistory(roomID, msgs))
结果是:
- 启动应用 → 聊天记录秒级加载
- 多聊天室数据完全隔离
- 图片/表情消息一样能储存
这是纯 Go + SQLite 的天然优势:轻、快、稳定。小应用的不二选择。
🎨UI 解析:前端生态在桌面端的全力释放
作为后端开发,我们要避开短板,选用一些成熟的样式框架,减少繁杂的css编写和调试。我采用 Vue3 + TailwindCSS + Pinia。
消息气泡部分(Chat.vue)示例:
<div ref="msgContainer" class="flex-1 overflow-auto p-6 bg-gradient-to-b from-white to-gray-50">
<div v-for="m in messages" :key="m.id" class="mb-4 flex" :class="m.username===username ? 'justify-end' : 'justify-start'">
<div :class="['max-w-[60%] p-4 rounded-2xl shadow-md', m.username===username ? 'bg-gradient-to-r from-indigo-500 to-indigo-600 text-white' : 'bg-white border border-gray-200 shadow-sm']">
<div class="flex items-center justify-between">
<div class="text-sm font-medium">{{ m.username }}</div>
<div class="text-xs text-gray-200" v-if="m.username===username">{{ formatTime(m.timestamp) }}</div>
</div>
<div class="mt-2">
<template v-if="m.type==='text'">
<div class="whitespace-pre-wrap">{{ m.content }}</div>
</template>
<template v-else-if="m.type==='emoji'">
<div class="text-2xl">{{ m.content }}</div>
</template>
<template v-else-if="m.type==='image'">
<img :src="m.content" class="w-48 h-48 object-cover rounded cursor-pointer" @click="viewImage(m.content)" />
</template>
<template v-else-if="m.type==='recall'">
<div class="italic text-sm text-gray-400">消息已撤回</div>
</template>
</div>
<div class="text-xs text-gray-400 mt-1" v-if="m.username!==username">{{ formatTime(m.timestamp) }}</div>
</div>
</div>
</div>
Vue 在桌面端的表现令人惊喜:
- 现代、漂亮的 UI 不再困难
- 热更新开发体验接近 Vite + 浏览器
- 任何前端 UI 库都能直接用
这就是为什么我说——
Wails 并不是“桌面界面框架”,而是“让前端生态进入桌面世界”。
🖼️发送图片 & 表情:Wails 让文件处理变得非常直接
前端读取本地文件:
function selectImage() {
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*'
input.onchange = () => {
if (input.files && input.files[0]) {
const file = input.files[0]
// 这里需要将文件转换为 base64 或者直接上传二进制
// 示例代码简化了读取过程
const reader = new FileReader()
reader.onload = (e) => {
// 假设 UploadImage 接受 base64 字符串
if(e.target?.result) {
UploadImage(room, e.target.result as string).then(url => SendMessage(room, url))
}
}
reader.readAsDataURL(file)
}
}
input.click()
}
后端接收图片:
func (c *ChatService) UploadImage(roomID string, dataBase64 string) (string, error) {
// 实际项目中需要解析 base64
// 这里仅作演示
filename := fmt.Sprintf("img_%d.png", time.Now().Unix())
path := filepath.Join(c.DataDir, filename)
// os.WriteFile(path, decodedData, 0644)
return filename, nil
}
没有浏览器沙盒限制,没有繁琐权限,桌面端体验极其自由。
🚀一键运行脚本:开发体验极其顺滑
#!/bin/bash
go mod tidy
wails dev
真正做到了:
- 克隆项目
- 执行脚本
- 直接启动桌面 IM
而整个应用打包后只有几十 MB。
对比 Electron 的 100+MB 起步,差距非常明显。
运行效果:
📌总结:为什么我认为 Wails 会火?
经过这次“聊天应用”实践,我对它的理解非常明确:
✔ 1. 前端界面能力 = 全能力释放(WEB 生态盘活)
你不是在堆原生控件,
你是在写 真正的现代前端界面。
✔ 2. Go 后端能力 = 稳、快、强(天然适合桌面)
构建速度快、并发强、跨平台优秀。
✔ 3. 体积小、启动快、无浏览器内核
最终打包体积小得离谱,启动速度秒开。
✔ 4. 简洁的 API(尤其是前后端通信)
IPC 简化到前端能直接调用 Go 方法。
✔ 5. 开发体验优秀(个人/团队都很适合)
前端热更新 + Go 构建速度 = 真香。
📦开源地址(含完整源码)
GitHub:
👉 github.com/louis-xie-p…
Gitee:
👉 gitee.com/louis_xie/c…
很多时候我们不是做不出桌面应用,
而是被历史的技术栈劝退了。
Wails 的出现,让我第一次真正感受到:
“桌面应用也可以像 Web 一样快、像 Go 一样强、像 Electron 一样灵活。”
如果你也是 Go 或前端开发者,
我真心建议你亲手试一次 Wails。
欢迎在评论区留言讨论你的使用体验和感受。
我相信你会非常惊喜。