🔄 第9章 状态管理进阶
单组件 @State 够用了吗?当应用变得复杂,状态需要跨组件、跨页面共享。本章系统梳理 ArkTS 的状态管理全景,从 @Provide/@Consume 到 AppStorage,掌握不同场景的最优选择。
10.1 9.1 状态管理全景
| 装饰器 | 作用范围 | 数据流向 | 典型场景 |
|---|---|---|---|
@State |
组件内部 | 单组件 | 按钮点击计数、输入框值 |
@Prop |
父→子 | 单向 | 展示组件接收配置 |
@Link |
父↔︎子 | 双向 | 子组件修改父数据 |
@Provide/@Consume |
祖先↔︎后代 | 双向跨层 | 主题色、语言设置 |
@Observed/@ObjectLink |
嵌套对象 | 深度监听 | 数组/对象属性变化 |
AppStorage |
全应用 | 全局共享 | 用户登录信息、主题 |
PersistentStorage |
全应用+持久化 | 跨启动 | 用户偏好设置 |
10.2 9.2 @Provide / @Consume —— 跨层级共享
无需一层一层传递 @Prop,祖先直接 @Provide,后代随时 @Consume:
// 祖先组件(App级别)
@Entry
@Component
struct AppRoot {
@Provide('theme') currentTheme: string = 'light'; // 提供数据
build() {
Column() {
ThemeToggle() // 切换主题的子组件
ContentArea() // 使用主题的深层子组件
}
}
}
// 中间层(不需要感知 theme)
@Component
struct ContentArea {
build() {
Column() {
DeepChild() // 深层子组件
}
}
}
// 深层子组件(直接消费)
@Component
struct DeepChild {
@Consume('theme') currentTheme: string; // 消费数据,无需逐层传递
build() {
Text("当前主题:" + this.currentTheme)
.fontColor(this.currentTheme === 'dark' ? '#fff' : '#333')
.backgroundColor(this.currentTheme === 'dark' ? '#1a1a1a' : '#fff')
}
}🎨 主题跨层传递演示
AppRoot(@Provide theme)
ContentArea(中间层,不感知 theme)
DeepChild(@Consume theme)
当前主题:light ☀️
10.3 9.3 @Observed / @ObjectLink —— 监听嵌套对象
@State 只能监听第一层变化,嵌套对象属性变化需要 @Observed:
@Observed
class User {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name; this.age = age;
}
}
@Component
struct UserCard {
@ObjectLink user: User; // 接收 @Observed 对象
build() {
Row() {
Text(this.user.name)
Text(`${this.user.age}岁`)
Button("+1岁").onClick(() => {
this.user.age++; // 修改嵌套属性 → 触发刷新!
})
}
}
}
@Entry
@Component
struct UserList {
@State users: User[] = [
new User("张三", 20),
new User("李四", 25),
];
build() {
Column() {
ForEach(this.users, (user: User) => {
UserCard({ user: user }) // 直接传 @Observed 对象
})
}
}
}⚠️ 常见误区
不加 @Observed 时,修改 user.age 不会触发 UI 刷新,因为 @State 只感知对象引用变化,不感知属性变化。
10.4 9.4 AppStorage —— 全应用状态
// 初始化(通常在 EntryAbility.ts 中)
AppStorage.setOrCreate('userId', 0);
AppStorage.setOrCreate('isLogin', false);
AppStorage.setOrCreate('userName', '游客');
// 组件中绑定
@Entry
@Component
struct HomePage {
// 双向绑定全局状态
@StorageLink('isLogin') isLogin: boolean = false;
@StorageLink('userName') userName: string = '游客';
build() {
Column() {
if (this.isLogin) {
Text(`欢迎回来,${this.userName}!`)
Button("退出登录").onClick(() => {
this.isLogin = false;
this.userName = '游客';
})
} else {
Button("登录").onClick(() => {
this.isLogin = true;
this.userName = '张三';
})
}
}
}
}🌐 AppStorage 全局状态演示
📦 AppStorage
isLogin: false
userName: "游客"
theme: "light"
请先登录
操作日志...
10.5 9.5 PersistentStorage —— 持久化存储
// 持久化存储:应用重启后数据仍存在
PersistentStorage.persistProp('colorMode', ColorMode.LIGHT);
PersistentStorage.persistProp('fontSize', 16);
PersistentStorage.persistProp('language', 'zh-CN');
@Entry
@Component
struct SettingsPage {
@StorageProp('fontSize') fontSize: number = 16; // 只读
@StorageLink('fontSize') fontSizeLink: number = 16; // 可写
build() {
Column({ space: 16 }) {
Text("字体大小设置")
.fontSize(this.fontSize) // 应用当前字号
Slider({ value: this.fontSizeLink, min: 12, max: 24 })
.onChange((v) => { this.fontSizeLink = v; }) // 修改 → 自动持久化
}
}
}10.6 9.6 状态选型指南
🤔 我该用哪种状态管理?
Q: 数据只在本组件内使用?
Q: 父组件传值给子组件,子组件只展示?
Q: 子组件需要修改父组件数据?
Q: 深层嵌套组件需要祖先数据?
Q: 全应用需要共享的状态(如登录信息)?
Q: 用户偏好设置,重启后也要保留?