跳到内容
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 是可以的,并且通常是首选的……异步 Mutex 的主要用例是为 I/O 资源(如数据库连接)提供共享的可变访问。

最好完整阅读链接的文档,以了解两者之间的权衡。您需要异步互斥锁的一个原因是,如果需要在 await 点之间持有 MutexGuard

在 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

有时您可能需要在命令之外访问状态,例如在不同的线程或事件处理程序(如 on_window_event)中。在这种情况下,您可以使用实现 Manager trait 的类型(例如 AppHandle)的 state() 方法来获取状态

use std::sync::Mutex;
use tauri::{Builder, Window, WindowEvent, Manager};
#[derive(Default)]
struct AppState {
counter: u32,
}
// In an event handler:
fn on_window_event(window: &Window, _event: &WindowEvent) {
// Get a handle to the app so we can get the global state.
let app_handle = 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