状态管理
@State/@Prop/@Link等装饰器
概述
HarmonyOS 的状态管理通过装饰器实现,不同装饰器控制数据的流向和同步范围。@State 管理组件内部状态,@Prop 和 @Link 实现父子组件通信,@Provide 和 @Consume 实现跨层级数据共享。
基础概念
状态装饰器对比
| 装饰器 | 数据流向 | 同步类型 | 适用场景 |
|---|---|---|---|
| @State | 组件内部 | 单向 | 组件私有状态 |
| @Prop | 父 -> 子 | 单向 | 子组件只读展示 |
| @Link | 父 <-> 子 | 双向 | 子组件需要修改父状态 |
| @Provide | 祖先 -> 后代 | 单向 | 跨层级共享 |
| @Consume | 消费祖先数据 | 双向 | 跨层级访问和修改 |
| @Watch | 监听变化 | - | 响应状态变化执行逻辑 |
| @StorageLink | AppStorage | 双向 | 应用级持久化状态 |
快速上手
@State 组件内部状态
@Entry
@Component
struct StateDemo {
@State count: number = 0
@State message: string = '点击按钮计数'
build() {
Column() {
Text(this.message).fontSize(20)
Text(`当前计数: ${this.count}`).fontSize(30).margin({ top: 20 })
Row() {
Button('-').onClick(() => this.count--)
Button('+').onClick(() => this.count++)
}.margin({ top: 20 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
@Prop 和 @Link 父子通信
// 子组件:使用 @Link 双向同步
@Component
struct Counter {
@Link count: number
build() {
Row() {
Button('-').onClick(() => this.count--)
Text(`${this.count}`).fontSize(24).margin({ left: 10, right: 10 })
Button('+').onClick(() => this.count++)
}
}
}
// 父组件
@Entry
@Component
struct Parent {
@State score: number = 0
build() {
Column() {
Text(`分数: ${this.score}`).fontSize(30)
// 使用 $ 语法传递 @Link 引用
Counter({ count: $score })
}
}
}
详细用法
@Provide 和 @Consume 跨层级通信
// 祖先组件提供数据
@Entry
@Component
struct GrandParent {
@Provide theme: string = 'light'
build() {
Column() {
Text(`当前主题: ${this.theme}`)
Button('切换主题')
.onClick(() => this.theme = this.theme === 'light' ? 'dark' : 'light')
// 中间层不需要传递 theme
MiddleLayer()
}
}
}
@Component
struct MiddleLayer {
build() {
Column() {
Text('中间层')
DeepChild()
}
}
}
// 深层组件消费数据
@Component
struct DeepChild {
@Consume theme: string
build() {
Text(`深层组件使用的主题: ${this.theme}`)
.fontColor(this.theme === 'dark' ? Color.White : Color.Black)
.backgroundColor(this.theme === 'dark' ? '#333' : '#FFF')
.padding(20)
}
}
@Watch 监听状态变化
@Entry
@Component
struct WatchDemo {
@State @Watch('onPriceChange') price: number = 100
@State @Watch('onQuantityChange') quantity: number = 1
@State total: number = 100
// 价格变化时重新计算总价
onPriceChange() {
this.total = this.price * this.quantity
}
// 数量变化时重新计算总价
onQuantityChange() {
this.total = this.price * this.quantity
}
build() {
Column() {
Text(`单价: ${this.price}`)
Slider({ value: this.price, min: 1, max: 1000 })
.onChange((value) => this.price = value)
Text(`数量: ${this.quantity}`)
Slider({ value: this.quantity, min: 1, max: 100 })
.onChange((value) => this.quantity = value)
Text(`总价: ${this.total}`).fontSize(30).fontWeight(FontWeight.Bold)
}.padding(20)
}
}
AppStorage 应用级状态
// 在 EntryAbility 中初始化 AppStorage
AppStorage.setOrCreate('token', '')
AppStorage.setOrCreate('userId', '')
// 在任意组件中使用 @StorageLink
@Entry
@Component
struct StorageDemo {
@StorageLink('token') token: string = ''
@StorageLink('userId') userId: string = ''
build() {
Column() {
Text(`用户ID: ${this.userId}`)
Text(`Token: ${this.token}`)
Button('登录')
.onClick(() => {
this.userId = 'user_123'
this.token = 'abc123xyz'
})
Button('退出')
.onClick(() => {
this.userId = ''
this.token = ''
})
}
}
}
常见场景
表单状态管理
@Entry
@Component
struct FormDemo {
@State username: string = ''
@State password: string = ''
@State isLoading: boolean = false
@State errorMsg: string = ''
build() {
Column() {
TextInput({ placeholder: '用户名' })
.onChange((value) => {
this.username = value
this.errorMsg = ''
})
TextInput({ placeholder: '密码' })
.type(InputType.Password)
.onChange((value) => {
this.password = value
this.errorMsg = ''
})
if (this.errorMsg) {
Text(this.errorMsg).fontColor(Color.Red).fontSize(14)
}
Button('登录')
.enabled(!this.isLoading)
.onClick(async () => {
this.isLoading = true
try {
await this.login()
} catch (e) {
this.errorMsg = '登录失败'
} finally {
this.isLoading = false
}
})
}.padding(20)
}
async login() {
// 登录逻辑
}
}
注意事项
- @State 修饰的变量类型变更会导致整个组件重新渲染,避免频繁变更引用类型
- @Prop 是单向的,子组件修改不会影响父组件
- @Link 使用 $ 语法传递引用,不是值传递
- @Provide/@Consume 通过变量名匹配,确保名称一致
- @Watch 回调在状态变更后同步执行,不要在回调中做耗时操作
- @StorageLink 修改会同步到 AppStorage,所有使用该 key 的组件都会更新
进阶用法
LocalStorage 页面级状态
// LocalStorage 是页面级的状态存储,比 AppStorage 作用范围更小
let storage: LocalStorage = new LocalStorage()
@Entry(storage)
@Component
struct LocalStorageDemo {
@LocalStorageLink('count') count: number = 0
build() {
Column() {
Text(`计数: ${this.count}`)
Button('增加').onClick(() => this.count++)
}
}
}