第十章 Tauri 架构全景#
"要驾驭一个框架,先要看透它的骨架。"
经过 Part 1 的 Rust 速成,你已经具备了阅读和编写 Rust 代码的能力。从本章开始,我们正式进入 Tauri 的世界。本章将从宏观视角剖析 Tauri 的架构设计,理解它为什么能做到"小而安全"。
mindmap
root((Tauri 架构全景))
双进程
Core Process
WebView Process
IPC Bridge
与 Electron 对比
不内嵌 Chromium
Rust 替代 Node
更小攻击面
安全模型
最小权限
capabilities
CSP
Hive 基础
命令
事件
状态
10.1 Tauri 与 Electron:架构对比#
在深入 Tauri 之前,我们先对比它与 Electron 的架构差异:
┌─────────────────────────────────────────────────────┐
│ Electron │
│ │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Main Process│ │ Renderer Process │ │
│ │ (Node.js) │◄──►│ (Chromium 完整内核) │ │
│ │ │IPC │ HTML / CSS / JS │ │
│ └─────────────┘ └─────────────────────────────┘ │
│ │
│ 打包体积:~150 MB+(含完整 Chromium + Node.js) │
│ 内存占用:~100-300 MB │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Tauri │
│ │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Core Process│ │ WebView Process │ │
│ │ (Rust) │◄──►│ (系统原生 WebView) │ │
│ │ │IPC │ HTML / CSS / JS │ │
│ └─────────────┘ └─────────────────────────────┘ │
│ │
│ 打包体积:~3-10 MB(复用系统 WebView) │
│ 内存占用:~30-80 MB │
└─────────────────────────────────────────────────────┘
关键差异#
维度 |
Electron |
Tauri |
|---|---|---|
后端语言 |
JavaScript (Node.js) |
Rust |
渲染引擎 |
自带 Chromium |
系统 WebView (WebKit/WebView2/WebKitGTK) |
打包体积 |
150 MB+ |
3-10 MB |
内存占用 |
100-300 MB |
30-80 MB |
安全模型 |
Node.js 拥有完整系统权限 |
最小权限 + 能力声明 |
跨平台 WebView |
一致(Chromium) |
不同平台不同引擎 |
生态成熟度 |
非常成熟 |
快速成长中 |
10.2 Tauri 的双进程模型#
Tauri 应用由两个核心进程组成:
┌──────────────────────────────────────────────────────────┐
│ Tauri 应用 │
│ │
│ ┌────────────────────────┐ IPC Bridge ┌─────────────┐ │
│ │ Core Process │◄────────────►│ WebView │ │
│ │ (Rust 后端) │ │ (前端 UI) │ │
│ │ │ invoke() │ │ │
│ │ • 业务逻辑 │◄─────────────│ • HTML │ │
│ │ • 文件系统访问 │ │ • CSS │ │
│ │ • 数据库操作 │ emit() │ • JS/TS │ │
│ │ • 网络请求 │─────────────►│ • React │ │
│ │ • 系统 API │ │ • Vue │ │
│ │ • 插件管理 │ listen() │ • Svelte │ │
│ │ • 安全策略 │◄─────────────│ │ │
│ └────────────────────────┘ └─────────────┘ │
│ │
│ macOS: WKWebView Windows: WebView2 Linux: WebKitGTK│
└──────────────────────────────────────────────────────────┘
10.2.1 Core Process(核心进程)#
Core Process 是用 Rust 编写的后端进程,负责:
应用生命周期管理 — 启动、运行、退出
窗口管理 — 创建、关闭、调整窗口
系统 API 访问 — 文件、网络、托盘、通知等
IPC 处理 — 接收前端请求,返回结果
安全策略执行 — 权限检查、CSP 策略
// src-tauri/src/main.rs — Core Process 入口
fn main() {
tauri::Builder::default()
// 注册插件
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_fs::init())
// 注册命令(供前端 invoke 调用)
.invoke_handler(tauri::generate_handler![
greet,
read_config,
save_note,
])
// 管理全局状态
.manage(AppState::default())
// 配置窗口创建回调
.setup(|app| {
println!("Hive 应用启动!");
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
10.2.2 WebView Process(WebView 进程)#
WebView Process 负责渲染用户界面:
macOS — 使用 WKWebView(Safari 内核)
Windows — 使用 WebView2(Edge/Chromium 内核)
Linux — 使用 WebKitGTK
// src/main.js — 前端入口
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
// 调用 Rust 后端命令
const result = await invoke('greet', { name: 'Walter' });
console.log(result); // "Hello, Walter!"
// 监听后端事件
await listen('file-changed', (event) => {
console.log('文件变更:', event.payload);
});
10.2.3 为什么是双进程?#
┌─────────────────────────────────────────────┐
│ 安全边界(Sandbox) │
│ │
│ WebView 进程 Core 进程 │
│ ┌───────────┐ ┌───────────┐ │
│ │ 只能渲染 │ IPC 桥 │ 可访问 │ │
│ │ HTML/CSS │◄────────────►│ 系统资源 │ │
│ │ /JS │ 权限检查 │ 文件/网络 │ │
│ │ │ │ 数据库 │ │
│ │ 无法直接 │ │ │ │
│ │ 访问文件系统│ │ 按需授权 │ │
│ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────┘
核心原则:最小权限(Principle of Least Privilege)
WebView 默认没有任何系统权限
每个能力都需要显式声明
Core Process 作为"守门人"验证每个请求
10.3 IPC 通信机制概览#
Tauri 提供两种 IPC 通信模式:
10.3.1 Command 模式(请求-响应)#
前端通过 invoke() 调用 Rust 命令,等待返回结果:
前端 (JS) 后端 (Rust)
│ │
│ invoke('save_note', {content}) │
│──────────────────────────────────►│
│ │ fn save_note(content)
│ │ → 写入文件
│ │ → 返回 Result
│ Ok("saved") / Err(...) │
│◄──────────────────────────────────│
│ │
Rust 端:
#[tauri::command]
async fn save_note(content: String) -> Result<String, String> {
std::fs::write("note.md", &content)
.map_err(|e| e.to_string())?;
Ok("saved".to_string())
}
前端:
try {
const result = await invoke('save_note', { content: '# Hello' });
console.log(result); // "saved"
} catch (error) {
console.error('保存失败:', error);
}
10.3.2 Event 模式(发布-订阅)#
适用于后端主动推送、双向通知的场景:
前端 (JS) 后端 (Rust)
│ │
│ listen('download-progress') │
│ ┌──────────────────────────┐ │
│ │ 注册监听器 │ │
│ └──────────────────────────┘ │
│ │
│ emit('download-progress', │
│ { percent: 30 }) │
│◄──────────────────────────────────│
│ │
│ emit('download-progress', │
│ { percent: 70 }) │
│◄──────────────────────────────────│
│ │
│ emit('download-progress', │
│ { percent: 100 }) │
│◄──────────────────────────────────│
Rust 端发送事件:
use tauri::Emitter;
#[tauri::command]
async fn download_file(app: tauri::AppHandle, url: String) -> Result<(), String> {
for i in (0..=100).step_by(10) {
// 模拟下载进度
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
app.emit("download-progress", serde_json::json!({
"percent": i
})).map_err(|e| e.to_string())?;
}
Ok(())
}
前端监听事件:
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('download-progress', (event) => {
const { percent } = event.payload;
updateProgressBar(percent);
});
// 不再需要时取消监听
// unlisten();
10.3.3 两种模式对比#
维度 |
Command (invoke) |
Event (emit/listen) |
|---|---|---|
方向 |
前端 → 后端 → 前端 |
双向 |
模式 |
请求-响应 |
发布-订阅 |
返回值 |
有(Result) |
无(fire-and-forget) |
适用场景 |
CRUD 操作、查询 |
进度通知、状态变更、实时推送 |
类比 |
REST API |
WebSocket / SSE |
10.4 Tauri 的安全模型#
安全是 Tauri 的核心设计理念之一。
10.4.1 能力系统(Capabilities)#
Tauri 2.0 引入了声明式的能力系统:
// src-tauri/capabilities/default.json
{
"identifier": "default",
"description": "默认能力集",
"windows": ["main"],
"permissions": [
"core:default",
"shell:allow-open",
"fs:allow-read-text-file",
"fs:allow-write-text-file",
{
"identifier": "fs:scope",
"allow": [
"$APPDATA/**",
"$DOCUMENT/**"
]
}
]
}
10.4.2 安全层次#
┌─────────────────────────────────────────────┐
│ Tauri 安全模型(四层防御) │
│ │
│ Layer 4: CSP(内容安全策略) │
│ ┌─────────────────────────────────────┐ │
│ │ 限制 JS 可加载的资源来源 │ │
│ └─────────────────────────────────────┘ │
│ │
│ Layer 3: Capabilities(能力声明) │
│ ┌─────────────────────────────────────┐ │
│ │ 声明窗口可使用的 API 权限 │ │
│ └─────────────────────────────────────┘ │
│ │
│ Layer 2: IPC 过滤 │
│ ┌─────────────────────────────────────┐ │
│ │ 只有注册的命令才能被调用 │ │
│ └─────────────────────────────────────┘ │
│ │
│ Layer 1: 进程隔离 │
│ ┌─────────────────────────────────────┐ │
│ │ WebView 无法直接访问系统资源 │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
10.4.3 与 Electron 安全对比#
维度 |
Electron |
Tauri |
|---|---|---|
默认权限 |
Node.js 拥有完整系统权限 |
WebView 默认无权限 |
权限控制 |
需手动配置 contextIsolation |
声明式 Capabilities |
远程代码 |
可通过 |
CSP 严格限制 |
文件访问 |
默认可访问整个文件系统 |
需声明 scope 范围 |
10.5 Tauri 应用的生命周期#
┌──────────────────────────────────────────────────┐
│ Tauri 应用生命周期 │
│ │
│ ┌─────────┐ │
│ │ 启动 │ main() → tauri::Builder │
│ └────┬────┘ │
│ ▼ │
│ ┌─────────┐ │
│ │ setup │ .setup(|app| { ... }) │
│ │ 初始化 │ 注册插件、初始化状态、创建窗口 │
│ └────┬────┘ │
│ ▼ │
│ ┌─────────┐ │
│ │ 运行 │ 事件循环(Event Loop) │
│ │ │ 处理 IPC、窗口事件、系统事件 │
│ └────┬────┘ │
│ ▼ │
│ ┌─────────┐ │
│ │ 退出 │ on_window_event(CloseRequested) │
│ │ │ 清理资源、保存状态 │
│ └─────────┘ │
└──────────────────────────────────────────────────┘
生命周期钩子#
fn main() {
tauri::Builder::default()
// 1. setup — 应用初始化
.setup(|app| {
println!("✅ 应用初始化");
// 获取应用数据目录
let app_dir = app.path().app_data_dir()
.expect("无法获取应用数据目录");
std::fs::create_dir_all(&app_dir).ok();
// 创建额外窗口
let _settings_window = tauri::WebviewWindowBuilder::new(
app,
"settings",
tauri::WebviewUrl::App("settings.html".into()),
)
.title("设置")
.inner_size(600.0, 400.0)
.build()?;
Ok(())
})
// 2. on_window_event — 窗口事件
.on_window_event(|window, event| {
match event {
tauri::WindowEvent::CloseRequested { api, .. } => {
println!("窗口 {} 请求关闭", window.label());
// 可以阻止关闭:api.prevent_close();
}
tauri::WindowEvent::Focused(focused) => {
println!("窗口 {} 焦点: {}", window.label(), focused);
}
_ => {}
}
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
10.6 Tauri 项目结构深度解析#
hive/
├── src-tauri/ # Rust 后端
│ ├── Cargo.toml # Rust 依赖管理
│ ├── tauri.conf.json # Tauri 核心配置
│ ├── capabilities/ # 能力声明(安全策略)
│ │ └── default.json
│ ├── icons/ # 应用图标
│ │ ├── icon.ico
│ │ ├── icon.png
│ │ └── icon.icns
│ ├── src/
│ │ ├── main.rs # 入口(桌面端)
│ │ ├── lib.rs # 核心库(可被测试引用)
│ │ ├── commands/ # IPC 命令模块
│ │ │ ├── mod.rs
│ │ │ ├── notes.rs
│ │ │ └── settings.rs
│ │ ├── models/ # 数据模型
│ │ │ ├── mod.rs
│ │ │ └── note.rs
│ │ └── state.rs # 全局状态
│ └── build.rs # 构建脚本
│
├── src/ # 前端代码
│ ├── index.html
│ ├── main.js # 或 main.ts
│ ├── styles.css
│ ├── components/
│ └── pages/
│
├── package.json # 前端依赖
└── vite.config.js # 前端构建配置
tauri.conf.json 核心配置#
{
"productName": "Hive",
"version": "0.1.0",
"identifier": "com.example.hive",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:5173",
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build"
},
"app": {
"windows": [
{
"label": "main",
"title": "Hive",
"width": 1024,
"height": 768,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src 'self'; script-src 'self'"
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/icon.icns",
"icons/icon.ico"
]
}
}
10.7 从 C++/Java 架构师视角看 Tauri#
与 C++ Qt 对比#
维度 |
Qt |
Tauri |
|---|---|---|
UI 技术 |
QML / Widgets (C++) |
HTML/CSS/JS |
后端语言 |
C++ |
Rust |
信号-槽 |
Qt Signal/Slot |
Command + Event |
跨平台 |
编译时适配 |
运行时 WebView 适配 |
包管理 |
qmake / CMake |
Cargo + npm |
学习曲线 |
陡峭(MOC、QObject 体系) |
中等(Web 前端 + Rust) |
与 Java Swing/JavaFX 对比#
维度 |
JavaFX |
Tauri |
|---|---|---|
UI 技术 |
FXML / CSS |
HTML/CSS/JS |
后端语言 |
Java |
Rust |
事件模型 |
EventHandler |
Command + Event |
打包 |
jlink / jpackage (~50 MB+) |
tauri build (~5 MB) |
性能 |
JVM 启动慢 |
原生启动快 |
10.8 Hive 项目架构设计#
在接下来的章节中,我们将逐步构建 Hive 项目。这里先给出整体架构:
┌─────────────────────────────────────────────────────┐
│ Hive 架构 │
│ │
│ ┌─────────────────────────────────────────────────┐│
│ │ 前端 (WebView) ││
│ │ ││
│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────────┐ ││
│ │ │ 笔记 │ │ 聊天 │ │ 设置 │ │ 插件面板 │ ││
│ │ │ 编辑器│ │ 窗口 │ │ 页面 │ │ │ ││
│ │ └──┬───┘ └──┬───┘ └──┬───┘ └────┬─────┘ ││
│ │ │ │ │ │ ││
│ │ └─────────┴─────────┴────────────┘ ││
│ │ │ invoke() / listen() ││
│ └────────────────────┼───────────────────────────┘│
│ │ IPC Bridge │
│ ┌────────────────────┼───────────────────────────┐│
│ │ 后端 (Rust) ││
│ │ │ ││
│ │ ┌────────────┐ ┌┴───────────┐ ┌──────────┐ ││
│ │ │ 命令处理器 │ │ 事件分发器 │ │ 插件管理器│ ││
│ │ └─────┬──────┘ └─────┬──────┘ └────┬─────┘ ││
│ │ │ │ │ ││
│ │ ┌─────┴───────────────┴───────────────┴─────┐ ││
│ │ │ 服务层 (Service) │ ││
│ │ │ NoteService ChatService ConfigService │ ││
│ │ └─────────────────┬─────────────────────────┘ ││
│ │ │ ││
│ │ ┌─────────────────┴─────────────────────────┐ ││
│ │ │ 存储层 (Storage) │ ││
│ │ │ SQLite FileSystem HTTP Client │ ││
│ │ └───────────────────────────────────────────┘ ││
│ └────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────┘
10.9 小结#
本章我们从宏观视角了解了 Tauri 的架构设计:
核心知识点#
概念 |
要点 |
|---|---|
双进程模型 |
Core Process (Rust) + WebView Process (系统原生) |
IPC 通信 |
Command(请求-响应)+ Event(发布-订阅) |
安全模型 |
四层防御:进程隔离 → IPC 过滤 → Capabilities → CSP |
生命周期 |
setup → 事件循环 → 退出清理 |
项目结构 |
src-tauri/ (Rust) + src/ (前端) + tauri.conf.json |
Tauri 设计哲学#
┌────────────────────────────────────────┐
│ Tauri 设计哲学 │
│ │
│ 🔒 安全优先 — 最小权限原则 │
│ 📦 轻量打包 — 复用系统 WebView │
│ 🦀 性能保障 — Rust 零成本抽象 │
│ 🌐 Web 生态 — 复用前端技术栈 │
│ 🔌 可扩展 — 插件系统 │
└────────────────────────────────────────┘
下一章预告#
第十一章我们将深入 前后端通信,详细学习 #[tauri::command] 的各种用法、参数传递、状态管理,以及事件系统的高级模式。