Tauri是一个用Rust编写的轻量级桌面应用框架,前端用Web技术,后端用Rust——与Electron相比,打包体积和内存占用可以降低一个数量级。
1. Tauri vs Electron
先看核心对比数据:
| 指标 | Tauri | Electron |
|---|---|---|
| Hello World安装包大小 | ~3 MB | ~85 MB |
| 运行内存占用 | ~30 MB | ~150 MB |
| 后端语言 | Rust | Node.js |
| 渲染引擎 | 系统WebView | 自带Chromium |
| 安全模型 | 默认最小权限 | 完整Node.js权限 |
Tauri使用操作系统自带的WebView(Windows上是WebView2/Edge,macOS是WKWebView,Linux是WebKitGTK),所以不需要打包整个Chromium。这也是它体积小的根本原因。
缺点是跨平台渲染一致性不如Electron,某些CSS特性在不同系统的WebView上表现可能有差异。
2. 项目初始化
前置条件
- Rust工具链(rustup安装)
- Node.js 16+
- 系统依赖:
- Windows:WebView2(Win10/11自带)
- macOS:Xcode Command Line Tools
- Linux:
libwebkit2gtk-4.0-dev,libgtk-3-dev
创建项目
# 使用官方脚手架
npm create tauri-app@latest
# 按提示选择:
# Project name: my-tauri-app
# Frontend framework: Vanilla / React / Vue / Svelte
# (以React为例)
生成的项目结构:
my-tauri-app/
├── src/ # 前端代码(React)
│ ├── App.tsx
│ └── main.tsx
├── src-tauri/ # Rust后端代码
│ ├── src/
│ │ └── main.rs # Rust入口
│ ├── Cargo.toml
│ └── tauri.conf.json # Tauri配置
├── package.json
└── vite.config.ts
启动开发服务器:
npm run tauri dev
第一次运行会编译Rust代码,稍等片刻后会弹出桌面窗口,内嵌了前端页面。支持前端热更新(HMR),Rust代码修改后也会自动重新编译。
3. 前端 + Rust后端架构
Tauri的架构很清晰:
┌──────────────────────────────┐
│ Desktop Window │
│ ┌────────────────────────┐ │
│ │ WebView (系统自带) │ │
│ │ ┌──────────────────┐ │ │
│ │ │ 前端 (React等) │ │ │
│ │ │ HTML/CSS/JS │ │ │
│ │ └───────┬──────────┘ │ │
│ │ │ invoke() │ │
│ │ ▼ │ │
│ │ ┌──────────────────┐ │ │
│ │ │ Rust Core │ │ │
│ │ │ (文件/网络/系统) │ │ │
│ │ └──────────────────┘ │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
前端通过invoke()函数调用Rust后端的命令。Rust处理文件操作、网络请求、系统调用等需要原生能力的任务。
4. #[tauri::command] —— 前后端通信
定义一个Rust命令:
// src-tauri/src/main.rs
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! This is from Rust.", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
前端调用:
import { invoke } from "@tauri-apps/api/tauri";
async function callRust() {
const result = await invoke<string>("greet", { name: "World" });
console.log(result); // "Hello, World! This is from Rust."
}
参数和返回值自动通过JSON序列化/反序列化。复杂类型需要实现serde::Serialize/Deserialize:
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct FileInfo {
name: String,
size: u64,
is_dir: bool,
}
#[tauri::command]
fn list_dir(path: String) -> Result<Vec<FileInfo>, String> {
let entries = std::fs::read_dir(&path)
.map_err(|e| e.to_string())?;
let mut files = Vec::new();
for entry in entries {
let entry = entry.map_err(|e| e.to_string())?;
let metadata = entry.metadata().map_err(|e| e.to_string())?;
files.push(FileInfo {
name: entry.file_name().to_string_lossy().to_string(),
size: metadata.len(),
is_dir: metadata.is_dir(),
});
}
Ok(files)
}
返回Result<T, String>时,Err会在前端变成rejected Promise。
5. 事件系统
除了command的请求-响应模式,Tauri还支持事件驱动通信:
Rust端发送事件:
use tauri::Manager;
#[tauri::command]
fn start_task(window: tauri::Window) {
std::thread::spawn(move || {
for i in 0..100 {
std::thread::sleep(std::time::Duration::from_millis(50));
window.emit("progress", i).unwrap();
}
window.emit("task-complete", "done").unwrap();
});
}
前端监听事件:
import { listen } from "@tauri-apps/api/event";
await listen<number>("progress", (event) => {
console.log(`Progress: ${event.payload}%`);
});
await listen<string>("task-complete", (event) => {
console.log(`Task completed: ${event.payload}`);
});
事件系统适合长时间运行的后台任务(文件复制、下载进度等)。
6. 打包发布
npm run tauri build
输出在src-tauri/target/nelease/bundle/下:
- Windows:
.msi安装包 和.exe - macOS:
.dmg和.app - Linux:
.deb、.AppImage
tauri.conf.json中配置应用信息:
{
"package": {
"productName": "My App",
"version": "1.0.0"
},
"tauri": {
"bundle": {
"identifier": "com.example.myapp",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/icon.ico"
]
},
"windows": [{
"title": "My Tauri App",
"width": 1024,
"height": 768,
"resizable": true
}]
}
}
小结
Tauri给Web前端开发者提供了一条通往原生桌面应用的轻量级路径。如果你的应用不需要像VS Code那样深度定制Chromium,Tauri在体积、性能和安全性上都优于Electron。学习成本主要在Rust,但对于简单的后端逻辑,上手门槛并不高。