状态管理
概述
状态管理是框架的核心能力之一,提供跨页面、跨模块的响应式状态共享机制。本框架强制使用 HarmonyOS V2 版本的状态管理 API(@ObservedV2、AppStorageV2、PersistenceV2),禁用所有 V1 版本 API。
核心特性
- 响应式更新:状态变化自动触发 UI 刷新,无需手动调用更新方法
- 全局共享:多个页面可以共享同一状态实例,实现数据同步
- 持久化支持:支持将状态持久化到本地存储,应用重启后自动恢复
- 类型安全:完整的 TypeScript 类型支持,编译时类型检查
适用场景
- 用户信息:登录状态、用户资料等需要全局访问的数据
- 应用配置:主题设置、语言偏好等应用级配置
- 业务状态:购物车、消息未读数等跨页面共享的业务数据
- 临时缓存:页面间传递的临时数据
核心装饰器
@ObservedV2
用于标记可观察的类,使其成为响应式状态容器。
typescript
@ObservedV2
export class CounterState {
@Trace
count: number = 0;
}使用规则:
- 必须用于状态类的类声明
- 类中需要响应式的字段必须使用
@Trace装饰 - 支持嵌套对象,嵌套对象也需要使用
@ObservedV2装饰
@Trace
用于标记需要追踪的响应式字段,只有标注 @Trace 的字段变化才会触发 UI 更新。
typescript
@ObservedV2
export class UserState {
@Trace
userName: string = ""; // 会触发 UI 更新
userId: number = 0; // 不会触发 UI 更新
}使用规则:
- 只能用于
@ObservedV2类的字段 - 基本类型(string、number、boolean)直接使用
@Trace - 复杂类型(对象、数组)需要配合
@Type使用
@Type
用于标注复杂类型字段,确保序列化和反序列化正确。
typescript
@ObservedV2
export class UserState {
@Type(User)
@Trace
userInfo: User = new User();
@Type(Auth)
@Trace
auth: Auth = new Auth();
}AppStorageV2:临时全局状态
AppStorageV2 用于存储应用运行期间的临时全局状态,应用关闭后数据会丢失。
基本用法
1. 定义状态类
typescript
import { AppStorageV2 } from "@kit.ArkUI";
/**
* AppStorageV2 键名
*/
export const DEMO_COUNTER_KEY: string = "demo_counter_state";
/**
* 全局计数器状态
*/
@ObservedV2
export class DemoCounterState {
/**
* 当前计数
*/
@Trace
count: number = 0;
/**
* 计数加一
*/
increment(step: number = 1): void {
this.count += step;
}
/**
* 计数减一
*/
decrement(step: number = 1): void {
if (this.count - step < 0) {
this.count = 0;
return;
}
this.count -= step;
}
/**
* 重置计数
*/
reset(resetValue: number = 0): void {
this.count = resetValue;
}
}2. 创建获取函数
typescript
/**
* 获取全局计数器状态实例;若不存在则创建
*/
export function getDemoCounterState(): DemoCounterState {
return AppStorageV2.connect<DemoCounterState>(
DemoCounterState,
DEMO_COUNTER_KEY,
() => new DemoCounterState()
)!;
}3. 在 ViewModel 中使用
typescript
@ObservedV2
export default class StateManagementViewModel extends BaseViewModel {
/**
* 计数器状态
*/
@Trace
counterState: DemoCounterState = getDemoCounterState();
/**
* 计数加一
*/
increment(): void {
this.counterState.increment();
}
}4. 在 View 中使用
typescript
@ComponentV2
export struct StateManagementPage {
@Local
private vm: StateManagementViewModel = new StateManagementViewModel();
build() {
Column() {
// 显示计数
Text(`${this.vm.counterState.count}`)
.fontSize(48)
.fontWeight(FontWeight.Bold);
// 增加按钮
Button("增加")
.onClick(() => {
this.vm.increment();
});
}
}
}AppStorageV2 API
typescript
// 连接或创建状态
AppStorageV2.connect<T>(
type: Type<T>, // 状态类类型
key: string, // 唯一键名
defaultCreator: () => T // 默认创建函数
): T | undefined
// 移除状态
AppStorageV2.remove(key: string): void
// 清空所有状态
AppStorageV2.clear(): voidPersistenceV2:持久化全局状态
PersistenceV2 用于存储需要持久化的全局状态,应用关闭后数据会保存到本地,下次启动时自动恢复。
基本用法
1. 定义状态类
typescript
import { PersistenceV2, Type } from "@kit.ArkUI";
import { contextConstant } from "@kit.AbilityKit";
/**
* PersistenceV2 键名
*/
export const USER_STATE_KEY: string = "user_state";
/**
* 全局用户状态
*/
@ObservedV2
export class UserState {
/**
* 认证信息
*/
@Type(Auth)
@Trace
private auth: Auth = new Auth();
/**
* 用户信息
*/
@Type(User)
@Trace
userInfo: User = new User();
/**
* 更新用户登录状态
*/
updateUserState(auth: Auth, user: User): void {
this.auth = Auth.fromResponse(auth);
this.userInfo = User.fromResponse(user);
this.persist();
}
/**
* 用户登出,清空状态
*/
logout(): void {
this.auth = new Auth();
this.userInfo = new User();
PersistenceV2.remove(USER_STATE_KEY);
}
/**
* 持久化当前用户状态
*/
private persist(): void {
PersistenceV2.save(USER_STATE_KEY);
}
}2. 创建获取函数
typescript
/**
* 获取或创建全局用户状态(持久化)
*/
export function getUserState(): UserState {
return PersistenceV2.globalConnect<UserState>({
type: UserState,
key: USER_STATE_KEY,
defaultCreator: () => new UserState(),
areaMode: contextConstant.AreaMode.EL3 // 加密存储区域
})!;
}
// 监听序列化错误
PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
});3. 在业务代码中使用
typescript
// 登录成功后保存用户状态
const userState = getUserState();
userState.updateUserState(authData, userData);
// 获取用户信息
const user = getUserState().getUserInfo();
// 登出
getUserState().logout();PersistenceV2 API
typescript
// 全局连接或创建持久化状态
PersistenceV2.globalConnect<T>(options: {
type: Type<T>, // 状态类类型
key: string, // 唯一键名
defaultCreator: () => T, // 默认创建函数
areaMode?: contextConstant.AreaMode // 存储区域模式
}): T | undefined
// 保存状态到本地
PersistenceV2.save(key: string): void
// 移除持久化状态
PersistenceV2.remove(key: string): void
// 监听序列化错误
PersistenceV2.notifyOnError(
callback: (key: string, reason: string, msg: string) => void
): void存储区域模式
typescript
contextConstant.AreaMode.EL1 // 公共区域,未加密
contextConstant.AreaMode.EL2 // 私有区域,设备级加密
contextConstant.AreaMode.EL3 // 私有区域,用户级加密(推荐)
contextConstant.AreaMode.EL4 // 私有区域,仅在解锁时可访问推荐使用 EL3:用户级加密,安全性高,适合存储敏感数据。
在 MVVM 架构中使用
架构层次
View (视图层)
↓ @Local 绑定
ViewModel (视图模型层)
↓ @Trace 引用
State (状态层)
↓ AppStorageV2/PersistenceV2
Global Storage (全局存储)完整示例
1. 定义全局状态(shared/state)
typescript
// shared/state/src/main/ets/ThemeState.ets
import { AppStorageV2 } from "@kit.ArkUI";
export const THEME_STATE_KEY: string = "theme_state";
@ObservedV2
export class ThemeState {
@Trace
isDarkMode: boolean = false;
toggleTheme(): void {
this.isDarkMode = !this.isDarkMode;
}
}
export function getThemeState(): ThemeState {
return AppStorageV2.connect<ThemeState>(
ThemeState,
THEME_STATE_KEY,
() => new ThemeState()
)!;
}2. 在 ViewModel 中使用(packages/xxx/viewmodel)
typescript
// packages/settings/src/main/ets/viewmodel/SettingsViewModel.ets
import { BaseViewModel } from "@core/base";
import { ThemeState, getThemeState } from "@shared/state";
@ObservedV2
export default class SettingsViewModel extends BaseViewModel {
@Trace
themeState: ThemeState = getThemeState();
/**
* 切换主题
*/
toggleTheme(): void {
this.themeState.toggleTheme();
}
/**
* 获取当前主题模式
*/
getCurrentTheme(): string {
return this.themeState.isDarkMode ? "深色模式" : "浅色模式";
}
}3. 在 View 中使用(packages/xxx/view)
typescript
// packages/settings/src/main/ets/view/SettingsPage.ets
import SettingsViewModel from "../viewmodel/SettingsViewModel";
@ComponentV2
export struct SettingsPage {
@Local
private vm: SettingsViewModel = new SettingsViewModel();
build() {
Column() {
Text(`当前主题:${this.vm.getCurrentTheme()}`)
.fontSize(16);
Button("切换主题")
.onClick(() => {
this.vm.toggleTheme();
});
}
}
}最佳实践
1. 状态类设计原则
typescript
// ✅ 推荐:单一职责,状态类只负责状态管理
@ObservedV2
export class CartState {
@Trace
items: CartItem[] = [];
addItem(item: CartItem): void {
this.items = [...this.items, item]; // 不可变更新
}
removeItem(id: string): void {
this.items = this.items.filter(item => item.id !== id);
}
}
// ❌ 避免:在状态类中处理复杂业务逻辑
@ObservedV2
export class CartState {
async checkout(): Promise<void> {
// 不应该在状态类中直接调用 API
await api.checkout();
}
}2. 不可变更新
typescript
// ✅ 推荐:使用不可变方式更新数组和对象
@ObservedV2
export class TodoState {
@Trace
todos: Todo[] = [];
addTodo(todo: Todo): void {
this.todos = [...this.todos, todo]; // 创建新数组
}
updateTodo(id: string, updates: Partial<Todo>): void {
this.todos = this.todos.map(todo =>
todo.id === id ? { ...todo, ...updates } : todo
);
}
}
// ❌ 避免:直接修改原数组
addTodo(todo: Todo): void {
this.todos.push(todo); // 可能不会触发 UI 更新
}3. 复杂类型处理
typescript
// ✅ 推荐:使用 @Type 装饰器标注复杂类型
@ObservedV2
export class OrderState {
@Type(Order)
@Trace
currentOrder: Order = new Order();
@Type(OrderItem)
@Trace
items: OrderItem[] = [];
}
// 确保嵌套类也使用 @ObservedV2
@ObservedV2
export class Order {
@Trace
orderId: string = "";
@Trace
totalAmount: number = 0;
}4. 状态持久化时机
typescript
@ObservedV2
export class UserState {
@Type(User)
@Trace
userInfo: User = new User();
// ✅ 推荐:在状态变更后立即持久化
updateUserInfo(user: User): void {
this.userInfo = User.fromResponse(user);
this.persist(); // 立即保存
}
private persist(): void {
PersistenceV2.save(USER_STATE_KEY);
}
}5. 状态访问封装
typescript
// ✅ 推荐:提供便捷的访问方法
@ObservedV2
export class UserState {
@Type(Auth)
@Trace
private auth: Auth = new Auth();
// 提供公共访问方法
getToken(): string | null {
return this.auth?.token ?? null;
}
isLoggedIn(): boolean {
return !!this.getToken();
}
hasValidToken(): boolean {
return !!this.auth?.token && !this.auth.isExpired();
}
}常见问题
1. 状态更新了但 UI 没有刷新
原因:字段没有使用 @Trace 装饰器。
typescript
// ❌ 错误:缺少 @Trace
@ObservedV2
export class CounterState {
count: number = 0; // UI 不会更新
}
// ✅ 正确:添加 @Trace
@ObservedV2
export class CounterState {
@Trace
count: number = 0; // UI 会更新
}2. 数组或对象更新后 UI 没有刷新
原因:直接修改了原对象,而不是创建新对象。
typescript
// ❌ 错误:直接修改原数组
addItem(item: string): void {
this.items.push(item); // 可能不会触发更新
}
// ✅ 正确:创建新数组
addItem(item: string): void {
this.items = [...this.items, item];
}3. 持久化失败
原因:复杂类型没有使用 @Type 装饰器。
typescript
// ❌ 错误:缺少 @Type
@ObservedV2
export class UserState {
@Trace
userInfo: User = new User(); // 序列化可能失败
}
// ✅ 正确:添加 @Type
@ObservedV2
export class UserState {
@Type(User)
@Trace
userInfo: User = new User();
}4. 多个页面状态不同步
原因:每个页面创建了独立的状态实例,而不是使用全局状态。
typescript
// ❌ 错误:每次都创建新实例
@ObservedV2
export class MyViewModel extends BaseViewModel {
@Trace
counterState: DemoCounterState = new DemoCounterState(); // 独立实例
}
// ✅ 正确:使用全局状态
@ObservedV2
export class MyViewModel extends BaseViewModel {
@Trace
counterState: DemoCounterState = getDemoCounterState(); // 全局共享
}5. 状态类中的方法无法访问
原因:状态类的方法没有正确绑定 this。
typescript
// ✅ 推荐:使用箭头函数或在调用时绑定 this
@ObservedV2
export class CounterState {
@Trace
count: number = 0;
// 方法 1:使用普通方法(推荐)
increment(): void {
this.count += 1;
}
// 方法 2:使用箭头函数
decrement = (): void => {
this.count -= 1;
};
}