菜单

状态管理

相关源文件

此 wiki 页面记录了 Tauri 的应用程序状态管理系统,该系统允许在您的应用程序中共享和访问全局状态。Tauri 中的状态管理允许您存储和访问在整个应用程序生命周期中需要可用的数据,例如数据库连接、配置设置或任何共享资源。

有关管理窗口状态的信息,请参阅 窗口管理,有关基于事件的通信,请参阅 事件系统

核心概念

Tauri 的状态管理围绕几个关键组件构建

  • StateManager:存储和管理所有应用程序状态的中央注册表
  • State<T>:一种 guard 类型,可提供对状态值的安全、类型化访问
  • Manager trait:提供注册和检索状态的方法

状态管理系统是基于类型的,而不是基于键的,这意味着您通过其 Rust 类型而不是字符串标识符来注册和检索状态值。

图示:状态管理架构

来源:crates/tauri/src/state.rs19-51 crates/tauri/src/lib.rs696-755

状态管理的工作原理

Tauri 的状态管理系统将值存储在线程安全的容器中,可以在应用程序的任何位置访问。 StateManager 维护一个从类型 ID 到实际值的映射,从而实现类型安全检索。

注册过程

当您使用 manage() 注册状态时,会发生以下情况

  1. 使用 Rust 的 TypeId::of<T>() 计算值的类型 ID
  2. 该值被装箱并固定(以确保其内存位置不变)
  3. 该值存储在状态管理器的内部映射中

检索过程

当您使用 state()try_state() 请求状态时

  1. 计算请求类型的类型 ID
  2. 状态管理器使用类型 ID 在其映射中查找该值
  3. 返回一个 State<T> guard,它提供了对底层值的安全访问

图示:状态生命周期

来源:crates/tauri/src/state.rs111-173 crates/tauri/src/lib.rs696-755

在应用程序中使用状态

注册状态

状态通常在应用程序设置期间注册

manage() 方法返回一个布尔值,指示状态是新注册的(true)还是已存在的(false)。

访问状态

状态可以通过几种方式访问

  1. 使用 state() 方法:

  2. 使用 try_state() 方法(如果找不到状态则不会 panic)

  3. 在命令处理程序中使用 State 参数:

图示:状态访问模式

来源:crates/tauri/src/lib.rs626-747 crates/tauri/src/state.rs19-51

状态可变性

重要的是,由于状态值在整个应用程序中共享并且必须是 Send + Sync + 'static,因此可变性只能通过内部可变性模式进行

图示:状态可变性模式

来源:crates/tauri/src/lib.rs626-655 crates/tauri/src/state.rs19-51

实现细节

StateManager

StateManager 是状态管理系统的核心。它维护一个从类型 ID 到值的映射

该映射由互斥锁保护以确保线程安全,并且值被固定以确保其内存位置保持稳定。

状态管理类型

State<T> 类型是对值引用的简单包装

它实现了 Deref 到底层类型,使其易于使用。

安全注意事项

StateManager 提供了一个 unmanage<T>() 方法,允许删除状态,但此方法不安全,并且自 2.3.0 版本以来已被弃用。删除状态可能会导致先前获得的 State 对象出现悬空引用。

更安全的选择是使用内部可变性模式,例如 Mutex<Option<T>>,其中可以在不删除状态本身的情况下获取内部值。

来源:crates/tauri/src/state.rs101-150 crates/tauri/src/lib.rs719-729

CommandArg 对 State 的实现

State<T> 类型实现了 CommandArg,允许将其用作命令处理程序中的参数

此实现允许在从 JavaScript 调用命令时自动提取状态。

来源:crates/tauri/src/state.rs60-70

AppManager 中的状态注册

应用程序管理器维护一个 Arc<StateManager>,用于在整个应用程序中访问状态

这使得任何拥有 AppManager 访问权限的组件都可以访问应用程序状态。

来源:crates/tauri/src/manager/mod.rs190-227

使用示例

这是一个定义、注册和使用状态的完整示例

来源:crates/tauri/src/lib.rs626-655 crates/tauri/src/state.rs19-51

内部架构

TypeIdMap 和 IdentHash

Tauri 使用一个专门的哈希映射来存储状态,该映射针对 TypeId 键进行了优化

IdentHash 是一个针对 TypeId 值(它们本身就是唯一的整数)进行性能优化的哈希器,它只是直接使用输入值作为哈希。

内存稳定性 Pinning

状态值使用 Pin<Box<dyn Any + Sync + Send>> 固定在内存中。这确保了存储值内存位置不变,这对于 state()try_state() 返回的引用的安全性很重要。

来源:crates/tauri/src/state.rs72-98 crates/tauri/src/state.rs101-110

性能考量

状态管理系统设计得非常高效,开销最小

  1. 基于 TypeId 的查找:使用 Rust 的类型系统进行快速查找
  2. 优化的哈希IdentHashTypeId 提供零开销哈希
  3. 基于引用的访问State<T> 提供引用,无需复制数据

设计应用程序时,请考虑

  • 粒度:定义将相关数据分组在一起的状态类型
  • 线程安全:确保您的状态类型使用适当的同步原语
  • 生命周期:状态的生命周期与应用程序的整个生命周期一样长,因此请避免使用非常大或资源密集型状态类型

来源:crates/tauri/src/state.rs72-98 crates/tauri/src/lib.rs696-755

最佳实践

  1. 使用内部可变性:对于需要修改的状态,始终使用内部可变性模式(MutexRwLock 等)
  2. 避免使用 unmanage:切勿使用已弃用的 unmanage 方法,因为它可能导致悬空引用
  3. 定义清晰的状态边界:将相关数据分组到内聚的状态类型中
  4. 尽早初始化:在应用程序设置期间注册状态
  5. 有防御性地使用 try_state:当不确定状态是否存在时,请使用 try_state 而不是 state 来避免 panic
  6. 考虑范围:仅对真正全局的数据使用全局状态;对与 UI 相关的数据使用特定于窗口的状态

来源: crates/tauri/src/lib.rs719-729 crates/tauri/src/lib.rs626-655