跳到內容
Tauri

狀態管理

在 Tauri 應用程式中,您通常需要追蹤應用程式的當前狀態或管理與其相關聯的事物的生命週期。Tauri 提供了一種使用 Manager API 管理應用程式狀態的簡便方法,並在呼叫命令時讀取它。

這是一個簡單的範例

use tauri::{Builder, Manager};
struct AppData {
welcome_message: &'static str,
}
fn main() {
Builder::default()
.setup(|app| {
app.manage(AppData {
welcome_message: "Welcome to Tauri!",
});
Ok(())
})
.run(tauri::generate_context!())
.unwrap();
}

稍後您可以使用任何實作 Manager trait 的類型存取您的狀態,例如 App 實例

let data = app.state::<AppData>();

有關更多資訊,包括在命令中存取狀態,請參閱存取狀態章節。

可變性

在 Rust 中,您無法直接變更在多個執行緒之間共享的值,或當所有權通過共享指標(例如 Arc (或 Tauri 的 State))控制時。這樣做可能會導致資料競爭(例如,同時發生兩個寫入)。

為了解決這個問題,您可以使用稱為內部可變性的概念。例如,標準函式庫的 Mutex 可用於包裝您的狀態。這允許您在需要修改值時鎖定它,並在完成後解鎖它。

use std::sync::Mutex;
use tauri::{Builder, Manager};
#[derive(Default)]
struct AppState {
counter: u32,
}
fn main() {
Builder::default()
.setup(|app| {
app.manage(Mutex::new(AppState::default()));
Ok(())
})
.run(tauri::generate_context!())
.unwrap();
}

現在可以通過鎖定互斥鎖來修改狀態

let state = app.state::<Mutex<AppState>>();
// Lock the mutex to get mutable access:
let mut state = state.lock().unwrap();
// Modify the state:
state.counter += 1;

在作用域結束時,或當 MutexGuard 以其他方式丟棄時,互斥鎖會自動解鎖,以便應用程式的其他部分可以存取和變更其中的資料。

何時使用非同步互斥鎖

引用 Tokio 文件,通常可以使用標準函式庫的 Mutex 來代替非同步互斥鎖,例如 Tokio 提供的那個

與普遍的看法相反,在非同步程式碼中使用標準函式庫中的普通 Mutex 是可以接受的,而且通常是首選的 ... 非同步互斥鎖的主要用例是為 IO 資源(例如資料庫連線)提供共享的可變存取權。

最好完整閱讀連結的文件,以了解兩者之間的權衡。您需要非同步互斥鎖的一個原因是,如果您需要在等待點之間持有 MutexGuard

您需要 Arc 嗎?

在 Rust 中,常見的做法是使用 Arc 在多個執行緒之間共享值的所有權(通常與 Mutex 結合使用,形式為 Arc<Mutex<T>>)。但是,您不需要為儲存在 State 中的事物使用 Arc,因為 Tauri 會為您執行此操作。

如果 State 的生命週期要求阻止您將狀態移動到新執行緒中,您可以改為將 AppHandle 移動到執行緒中,然後按照以下“使用 Manager trait 存取狀態”章節中所示的方式檢索您的狀態。AppHandle 經過刻意設計,可以廉價地複製以用於此類用例。

存取狀態

在命令中存取狀態

#[tauri::command]
fn increase_counter(state: State<'_, Mutex<AppState>>) -> u32 {
let mut state = state.lock().unwrap();
state.counter += 1;
state.counter
}

有關命令的更多資訊,請參閱從前端呼叫 Rust

非同步命令

如果您正在使用 async 命令並想要使用 Tokio 的非同步 Mutex,您可以像這樣設定它並存取狀態

#[tauri::command]
async fn increase_counter(state: State<'_, Mutex<AppState>>) -> Result<u32, ()> {
let mut state = state.lock().await;
state.counter += 1;
Ok(state.counter)
}

請注意,如果您使用非同步命令,則傳回類型必須是 Result

使用 Manager trait 存取狀態

有時您可能需要在命令之外存取狀態,例如在不同的執行緒中或在事件處理程序中,例如 on_window_event。在這種情況下,您可以使用實作 Manager trait 的類型(例如 AppHandle)的 state() 方法來取得狀態

use tauri::{Builder, GlobalWindowEvent, Manager};
#[derive(Default)]
struct AppState {
counter: u32,
}
// In an event handler:
fn on_window_event(event: GlobalWindowEvent) {
// Get a handle to the app so we can get the global state.
let app_handle = event.window().app_handle();
let state = app_handle.state::<Mutex<AppState>>();
// Lock the mutex to mutably access the state.
let mut state = state.lock().unwrap();
state.counter += 1;
}
fn main() {
Builder::default()
.setup(|app| {
app.manage(Mutex::new(AppState::default()));
Ok(())
})
.on_window_event(on_window_event)
.run(tauri::generate_context!())
.unwrap();
}

當您不能依賴命令注入時,此方法很有用。例如,如果您需要將狀態移動到使用 AppHandle 更容易的執行緒中,或者如果您不在命令上下文中。

類型不符

如果您願意,可以使用類型別名包裝您的狀態以防止此錯誤

use std::sync::Mutex;
#[derive(Default)]
struct AppStateInner {
counter: u32,
}
type AppState = Mutex<AppStateInner>;

但是,請確保按原樣使用類型別名,而不是再次將其包裝在 Mutex 中,否則您將遇到相同的問題。


© 2025 Tauri 貢獻者。CC-BY / MIT