状态管理 #
一、状态管理概述 #
1.1 什么是状态管理 #
状态管理是指在应用中管理和共享数据的方式。在 Tauri 应用中,状态可能需要在多个命令之间共享,或者需要在前后端之间同步。
text
┌─────────────────────────────────────────────────────┐
│ 应用状态 │
├─────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐│
│ │ 全局状态 │ │ 窗口状态 │ │ 用户状态 ││
│ │ AppState │ │ WindowState │ │ UserState ││
│ └─────────────┘ └─────────────┘ └─────────────┘│
│ │ │ │ │
│ └───────────────┼───────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ 状态管理器 │ │
│ │ (State Manager) │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
1.2 状态类型 #
| 类型 | 说明 | 生命周期 |
|---|---|---|
| 全局状态 | 整个应用共享 | 应用启动到关闭 |
| 窗口状态 | 单个窗口独享 | 窗口创建到关闭 |
| 会话状态 | 用户会话期间 | 用户登录到登出 |
| 持久化状态 | 保存到磁盘 | 永久存储 |
二、基本状态管理 #
2.1 定义状态结构 #
rust
use std::sync::Mutex;
// 应用全局状态
struct AppState {
counter: Mutex<i32>,
theme: Mutex<String>,
}
// 用户状态
struct UserState {
current_user: Mutex<Option<User>>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
2.2 注册状态 #
rust
fn main() {
tauri::Builder::default()
.manage(AppState {
counter: Mutex::new(0),
theme: Mutex::new("dark".to_string()),
})
.manage(UserState {
current_user: Mutex::new(None),
})
.invoke_handler(tauri::generate_handler![
get_counter,
increment_counter,
set_theme,
get_current_user,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
2.3 访问状态 #
rust
#[tauri::command]
fn get_counter(state: tauri::State<AppState>) -> i32 {
*state.counter.lock().unwrap()
}
#[tauri::command]
fn increment_counter(state: tauri::State<AppState>) -> i32 {
let mut counter = state.counter.lock().unwrap();
*counter += 1;
*counter
}
#[tauri::command]
fn set_theme(state: tauri::State<AppState>, theme: String) {
let mut current_theme = state.theme.lock().unwrap();
*current_theme = theme;
}
三、线程安全 #
3.1 使用 Mutex #
rust
use std::sync::Mutex;
struct AppState {
data: Mutex<Vec<String>>,
}
#[tauri::command]
fn add_item(state: tauri::State<AppState>, item: String) {
let mut data = state.data.lock().unwrap();
data.push(item);
}
#[tauri::command]
fn get_items(state: tauri::State<AppState>) -> Vec<String> {
let data = state.data.lock().unwrap();
data.clone()
}
3.2 使用 RwLock #
rust
use std::sync::RwLock;
struct AppState {
config: RwLock<Config>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize)]
struct Config {
theme: String,
language: String,
}
#[tauri::command]
fn get_config(state: tauri::State<AppState>) -> Config {
state.config.read().unwrap().clone()
}
#[tauri::command]
fn update_config(state: tauri::State<AppState>, config: Config) {
let mut current = state.config.write().unwrap();
*current = config;
}
3.3 使用 Arc #
rust
use std::sync::{Arc, Mutex};
struct AppState {
shared_data: Arc<Mutex<SharedData>>,
}
#[derive(Default)]
struct SharedData {
items: Vec<String>,
}
#[tauri::command]
fn process_shared(state: tauri::State<AppState>) -> usize {
let data = state.shared_data.lock().unwrap();
data.items.len()
}
四、异步状态操作 #
4.1 异步锁 #
rust
use tokio::sync::Mutex;
struct AsyncState {
data: Mutex<Vec<String>>,
}
#[tauri::command]
async fn async_add_item(state: tauri::State<'_, AsyncState>, item: String) {
let mut data = state.data.lock().await;
data.push(item);
}
#[tauri::command]
async fn async_get_items(state: tauri::State<'_, AsyncState>) -> Vec<String> {
let data = state.data.lock().await;
data.clone()
}
4.2 异步初始化 #
rust
struct AppState {
initialized: Mutex<bool>,
data: Mutex<Option<Data>>,
}
#[tauri::command]
async fn ensure_initialized(state: tauri::State<'_, AppState>) -> Result<(), String> {
let mut initialized = state.initialized.lock().await;
if !*initialized {
// 执行初始化
let data = load_data().await?;
*state.data.lock().await = Some(data);
*initialized = true;
}
Ok(())
}
async fn load_data() -> Result<Data, String> {
// 异步加载数据
Ok(Data { /* ... */ })
}
五、状态持久化 #
5.1 使用 tauri-plugin-store #
rust
// 安装插件
tauri::Builder::default()
.plugin(tauri_plugin_store::Builder::default().build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
typescript
// 前端使用
import { Store } from '@tauri-apps/plugin-store';
const store = new Store('settings.json');
// 保存数据
await store.set('theme', 'dark');
await store.set('user', { name: 'Alice' });
// 读取数据
const theme = await store.get<string>('theme');
const user = await store.get<{ name: string }>('user');
// 删除数据
await store.delete('theme');
// 保存到磁盘
await store.save();
5.2 自定义持久化 #
rust
use std::path::PathBuf;
use std::fs;
struct PersistentState {
path: PathBuf,
data: Mutex<Config>,
}
impl PersistentState {
fn new(path: PathBuf) -> Self {
let data = if path.exists() {
let content = fs::read_to_string(&path).unwrap_or_default();
serde_json::from_str(&content).unwrap_or_default()
} else {
Config::default()
};
Self {
path,
data: Mutex::new(data),
}
}
fn save(&self) -> Result<(), String> {
let data = self.data.lock().unwrap();
let content = serde_json::to_string_pretty(&*data)
.map_err(|e| e.to_string())?;
fs::write(&self.path, content)
.map_err(|e| e.to_string())
}
}
#[tauri::command]
fn save_config(state: tauri::State<PersistentState>) -> Result<(), String> {
state.save()
}
六、状态变更通知 #
6.1 事件通知 #
rust
use tauri::Emitter;
#[tauri::command]
async fn update_user(
app: tauri::AppHandle,
state: tauri::State<'_, UserState>,
user: User,
) -> Result<(), String> {
// 更新状态
let mut current = state.current_user.lock().unwrap();
*current = Some(user.clone());
// 发送事件通知前端
app.emit("user-changed", &user)
.map_err(|e| e.to_string())?;
Ok(())
}
typescript
// 前端监听事件
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen<User>('user-changed', (event) => {
console.log('User updated:', event.payload);
updateUserUI(event.payload);
});
// 取消监听
unlisten();
6.2 响应式状态 #
rust
struct ReactiveState {
value: Mutex<i32>,
subscribers: Mutex<Vec<tauri::Window>>,
}
#[tauri::command]
fn subscribe(window: tauri::Window, state: tauri::State<ReactiveState>) {
state.subscribers.lock().unwrap().push(window);
}
#[tauri::command]
fn update_value(state: tauri::State<ReactiveState>, value: i32) {
*state.value.lock().unwrap() = value;
// 通知所有订阅者
for window in state.subscribers.lock().unwrap().iter() {
let _ = window.emit("value-updated", value);
}
}
七、前端状态同步 #
7.1 状态同步 Hook #
typescript
// hooks/useAppState.ts
import { useState, useEffect } from 'react';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
export function useAppState<T>(key: string, defaultValue: T) {
const [state, setState] = useState<T>(defaultValue);
useEffect(() => {
// 加载初始状态
invoke<T>(`get_${key}`).then(setState);
// 监听状态变更
const unlisten = listen<T>(`${key}-changed`, (event) => {
setState(event.payload);
});
return () => {
unlisten.then((fn) => fn());
};
}, [key]);
const updateState = async (value: T) => {
await invoke(`set_${key}`, { value });
setState(value);
};
return [state, updateState] as const;
}
7.2 使用示例 #
typescript
// 组件中使用
function Settings() {
const [theme, setTheme] = useAppState('theme', 'light');
return (
<div>
<p>Current theme: {theme}</p>
<button onClick={() => setTheme('dark')}>Dark</button>
<button onClick={() => setTheme('light')}>Light</button>
</div>
);
}
八、状态验证 #
8.1 状态验证 #
rust
struct ValidatedState {
email: Mutex<String>,
}
#[tauri::command]
fn set_email(state: tauri::State<ValidatedState>, email: String) -> Result<(), String> {
// 验证邮箱格式
if !email.contains('@') {
return Err("Invalid email format".to_string());
}
let mut current = state.email.lock().unwrap();
*current = email;
Ok(())
}
8.2 状态转换 #
rust
#[derive(Debug, Clone, serde::Serialize)]
enum AppStateEnum {
Loading,
Ready,
Error(String),
}
struct AppState {
status: Mutex<AppStateEnum>,
}
#[tauri::command]
fn transition_state(
state: tauri::State<AppState>,
new_status: AppStateEnum,
) {
let mut status = state.status.lock().unwrap();
*status = new_status;
}
九、状态快照 #
9.1 创建快照 #
rust
#[derive(Clone, serde::Serialize)]
struct StateSnapshot {
counter: i32,
theme: String,
timestamp: u64,
}
#[tauri::command]
fn create_snapshot(state: tauri::State<AppState>) -> StateSnapshot {
StateSnapshot {
counter: *state.counter.lock().unwrap(),
theme: state.theme.lock().unwrap().clone(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
}
}
9.2 恢复快照 #
rust
#[tauri::command]
fn restore_snapshot(
state: tauri::State<AppState>,
snapshot: StateSnapshot,
) {
*state.counter.lock().unwrap() = snapshot.counter;
*state.theme.lock().unwrap() = snapshot.theme;
}
十、最佳实践 #
10.1 状态组织 #
rust
// 推荐:按功能模块组织状态
mod states {
pub struct UserState { /* ... */ }
pub struct ConfigState { /* ... */ }
pub struct CacheState { /* ... */ }
}
// 不推荐:单一巨大状态
struct AppState {
user: Option<User>,
config: Config,
cache: HashMap<String, String>,
// ... 太多字段
}
10.2 状态访问封装 #
rust
impl AppState {
fn get_counter(&self) -> i32 {
*self.counter.lock().unwrap()
}
fn set_counter(&self, value: i32) {
*self.counter.lock().unwrap() = value;
}
fn increment(&self) -> i32 {
let mut counter = self.counter.lock().unwrap();
*counter += 1;
*counter
}
}
10.3 错误处理 #
rust
#[tauri::command]
fn safe_operation(state: tauri::State<AppState>) -> Result<String, String> {
let data = state.data.lock()
.map_err(|_| "Failed to acquire lock".to_string())?;
// 安全访问 data
Ok(format!("{:?}", *data))
}
十一、调试技巧 #
11.1 状态日志 #
rust
#[tauri::command]
fn debug_state(state: tauri::State<AppState>) -> String {
let counter = *state.counter.lock().unwrap();
let theme = state.theme.lock().unwrap().clone();
format!("Counter: {}, Theme: {}", counter, theme)
}
11.2 状态监控 #
rust
struct MonitoredState {
value: Mutex<i32>,
access_count: Mutex<u32>,
}
#[tauri::command]
fn monitored_access(state: tauri::State<MonitoredState>) -> i32 {
*state.access_count.lock().unwrap() += 1;
*state.value.lock().unwrap()
}
十二、总结 #
12.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 状态定义 | 使用结构体定义状态 |
| 状态注册 | 使用 .manage() 注册 |
| 状态访问 | 使用 tauri::State 参数 |
| 线程安全 | 使用 Mutex/RwLock |
| 持久化 | 使用 tauri-plugin-store |
12.2 下一步 #
现在你已经掌握了 Tauri 的状态管理,接下来让我们学习 事件系统,了解应用内的事件驱动通信!
最后更新:2026-03-28