跳到内容
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 资源(如数据库连接)提供共享的可变访问。

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

异步命令

如果您正在使用异步命令并想使用 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 访问状态 Manager trait

有时您可能需要在命令之外访问状态,例如在不同的线程中或在事件处理程序(如 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