状态管理 #

一、状态管理概述 #

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