用 Tauri 写一个 AI Chat:3MB 的桌面应用干翻 Electron 100MB
用 Tauri 写一个 AI Chat:3MB 的桌面应用干翻 Electron 100MB
我之前做了一个网页版 AI Chat(纯浏览器直连 API),遇到两个大问题:API key 暴露在前端、CORS 到处报错。Electron?打包 100MB 起,内存占用动不动 500MB。
上周发现了 Tauri 2.0,用 Rust 做后端、Web 做前端,打包产物 3MB,内存占用不到 50MB。花了一个下午写了个 AI Chat 桌面应用,API key 安全存在 Rust 后端,零 CORS 问题,还支持流式输出。
这篇文章把整个过程拆解给你,从安装到打包发布,每一步都能跟着跑。
本文提纲
- Tauri 是什么、为什么比 Electron 好
- 环境准备与项目初始化
- Rust 后端:API 代理与流式处理
- Svelte 前端:聊天 UI 搭建
- 前后端通信:invoke 与事件系统
- 打包与发布
Tauri 是什么、为什么比 Electron 好
Tauri 的核心思路:用系统自带的 WebView 渲染 UI,用 Rust 处理后端逻辑。
Electron 的做法是把一整个 Chromium 打包进应用,所以体积大、内存高。Tauri 不带浏览器,直接调用操作系统的 WebView(macOS 用 WebKit、Windows 用 WebView2、Linux 用 WebKitGTK),省掉了几十 MB 的运行时。
graph LR
subgraph "Tauri Architecture"
A[WebView
Svelte/React/Vue] -->|IPC| B[Rust Backend
System API + HTTP]
end
subgraph "Electron Architecture"
C[Chromium
Full Browser] -->|Node.js| D[Main Process]
end关键数据对比:
| 指标 | Tauri 2.0 | Electron |
|---|---|---|
| 打包体积 | 3-10 MB | 100-200 MB |
| 空载内存 | ~30-50 MB | ~200-500 MB |
| 启动速度 | < 1s | 2-5s |
| 语言 | Rust(内存安全) | Node.js |
| 移动端 | iOS + Android | 不支持 |
| 安全模型 | 最小权限(白名单) | 全权限 |
还有一个容易被忽略的好处:Rust 的性能让 API 代理几乎零开销。同样的流式转发,Node.js 需要处理 backpressure 和内存管理,Rust 天然高效。
但也要诚实说 Tauri 的缺点:Rust 学习曲线陡,生态不如 Node.js 丰富,遇到 WebView 兼容性问题时排查更麻烦。如果你团队没有 Rust 经验,Electron 的开发效率可能更高。
环境准备与项目初始化
第 1 步:安装依赖
macOS:
xcode-select --install
rustup-initWindows:
# 安装 Visual Studio C++ Build Tools
# 安装 WebView2(Windows 11 自带,Windows 10 需手动装)
# 安装 Rust: https://rustup.rsLinux (Ubuntu):
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh第 2 步:创建项目
npm create tauri-app@latest ai-chat -- --template svelte-ts
cd ai-chat
npm install项目结构长这样:
ai-chat/
├── src/ # Svelte 前端
│ ├── App.svelte
│ ├── main.ts
│ └── app.css
├── src-tauri/ # Rust 后端
│ ├── src/
│ │ └── main.rs # Rust 入口
│ ├── Cargo.toml # Rust 依赖
│ └── tauri.conf.json # Tauri 配置
├── package.json
└── vite.config.ts第 3 步:验证能跑
npm run tauri dev第一次运行会编译 Rust 依赖,比较慢(2-5 分钟)。之后热更新很快,前端秒级刷新。
看到窗口弹出来就说明环境 OK。
Rust 后端:API 代理与流式处理
这是整个应用最关键的部分。Rust 后端负责:调用 AI API、处理流式响应、通过事件推送给前端。
安装 Rust 依赖
编辑 src-tauri/Cargo.toml:
[dependencies]
tauri = { version = "2", features = [] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.12", features = ["json", "stream"] }
tokio = { version = "1", features = ["full"] }
futures = "0.3"定义数据结构
在 src-tauri/src/main.rs 顶部:
use serde::{Deserialize, Serialize};
use tauri::Emitter;
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Message {
role: String,
content: String,
}
#[derive(Debug, Deserialize)]
struct ChatRequest {
messages: Vec,
model: String,
api_key: String,
api_url: String,
} 实现 API 调用命令
#[tauri::command]
async fn send_chat(
app: tauri::AppHandle,
request: ChatRequest,
) -> Result {
let client = reqwest::Client::new();
let body = serde_json::json!({
"model": request.model,
"messages": request.messages,
"stream": true,
"max_tokens": 4096,
});
let response = client
.post(&request.api_url)
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", request.api_key))
.json(&body)
.send()
.await
.map_err(|e| format!("Request failed: {}", e))?;
let mut stream = response.bytes_stream();
use futures::StreamExt;
let mut full_content = String::new();
while let Some(chunk) = stream.next().await {
let chunk = chunk.map_err(|e| format!("Stream error: {}", e))?;
let text = String::from_utf8_lossy(&chunk);
for line in text.lines() {
if line.starts_with("data: ") {
let data = &line[6..];
if data == "[DONE]" {
continue;
}
if let Ok(parsed) = serde_json::from_str::(data) {
if let Some(content) = parsed["choices"][0]["delta"]["content"].as_str() {
full_content.push_str(content);
let _ = app.emit("chat-chunk", content);
}
}
}
}
}
Ok(full_content)
} 这段代码做了三件事:
- 用
reqwest发送 POST 请求到 AI API(支持 OpenAI 兼容的任何 API) - 解析 SSE(Server-Sent Events)流式响应
- 每收到一个 chunk 就通过
app.emit("chat-chunk", content)推送给前端
注册命令
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![send_chat])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}Svelte 前端:聊天 UI 搭建
前端就是普通的 Svelte 应用,唯一的区别是调 API 用 Tauri 的 invoke 而不是 fetch。
安装 Tauri API 包
npm install @tauri-apps/api聊天主界面
替换 src/App.svelte:
前后端通信:invoke 与事件系统
上面的代码展示了 Tauri 前后端通信的两种方式,值得单独说一下:
方式一:invoke(请求-响应)
前端调用 Rust 命令,等返回结果:
// 前端
const result = await invoke("send_chat", { request: { ... } });
// 对应 Rust
#[tauri::command]
async fn send_chat(request: ChatRequest) -> Result {
// ...
} 参数名必须一致:前端传 request,Rust 函数参数也叫 request。
方式二:事件系统(实时推送)
Rust 端发射事件,前端监听:
// Rust 端发射
app.emit("chat-chunk", content)?;// 前端监听
const unlisten = await listen("chat-chunk", (event) => {
console.log(event.payload); // 收到的 chunk
});
// 用完记得清理
unlisten(); 这种模式特别适合流式输出:Rust 从 API 收到一个 chunk 就推一个,前端实时追加显示。
权限配置
Tauri 2.0 的安全模型默认禁止一切。需要在 src-tauri/capabilities/default.json 中声明权限:
{
"identifier": "default",
"description": "Default permissions",
"windows": ["main"],
"permissions": [
"core:default",
"core:event:default",
"core:event:allow-listen",
"core:event:allow-emit"
]
}打包与发布
开发模式
npm run tauri dev构建正式包
npm run tauri build构建产物在 src-tauri/target/release/bundle/:
| 平台 | 格式 | 大小 |
|---|---|---|
| macOS | .dmg / .app |
~8 MB |
| Windows | .msi / .exe |
~5 MB |
| Linux | .deb / .AppImage |
~4 MB |
对比 Electron 打包动辄 100MB+,Tauri 这个体积让人心情很好。
图标和元信息
在 src-tauri/tauri.conf.json 里配置:
{
"productName": "AI Chat",
"version": "0.1.0",
"identifier": "com.example.ai-chat",
"app": {
"windows": [
{
"title": "AI Chat",
"width": 900,
"height": 700,
"resizable": true
}
]
}
}如果想自定义图标(应用图标、安装器图标),准备一张 1024x1024 的 PNG,然后:
npm run tauri icon path/to/icon.png自动生成各平台各尺寸的图标。
跑完 npm run tauri dev 就能看到一个完整的 AI 聊天桌面应用。改 API URL 可以切到 DeepSeek、Claude、本地 Ollama,任何 OpenAI 兼容的 API 都行。
完整的源码思路都在上面了,可以直接复制粘贴跑起来。如果要加功能——对话历史持久化、Markdown 渲染、多 Agent 切换——都是普通的前端活,跟 Tauri 没关系了。
想动手试试的话,从 npm create tauri-app@latest 开始,半小时内就能看到第一个版本。
作者: itech001
来源: 公众号:AI人工智能时代
主页: https://www.theaiera.cn(每日分享最前沿的AI新闻和技术)
本文首发于 AI人工智能时代,转载请注明出处。