HarmonyOS 5开发从入门到精通(四):状态管理与数据绑定
HarmonyOS 5开发从入门到精通(四):状态管理与数据绑定
一、状态管理基础概念
在HarmonyOS应用开发中,状态管理是声明式UI的核心机制。状态(State)是驱动UI更新的数据,当状态发生变化时,框架会自动重新渲染相关的UI部分。
1.1 状态与视图的关系
状态管理的基本原理是:UI = f(State),即UI是状态的函数。当状态改变时,UI会自动更新。
状态变量vs常规变量:
- 状态变量:被装饰器装饰的变量,变化时触发UI更新
- 常规变量:普通变量,变化不会引起UI刷新
二、组件级状态管理
2.1 @State:组件内部状态
@State是组件内部的状态管理装饰器,用于管理组件的私有状态。
@Entry
@Component
struct CounterExample {
@State count: number = 0
@State isActive: boolean = false
build() {
Column({ space: 20 }) {
Text(`计数: ${this.count}`)
.fontSize(24)
.fontColor(this.isActive ? '#007DFF' : '#666')
Button('增加计数')
.onClick(() => {
this.count++
this.isActive = true
})
.width(200)
Button('重置')
.onClick(() => {
this.count = 0
this.isActive = false
})
.width(200)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
@State的特点:
- 组件私有,只能在组件内部访问
- 支持基本类型(number、string、boolean)和复杂类型
- 变化时自动触发UI更新
2.2 @Prop:父子组件单向同步
@Prop用于父组件向子组件传递数据,建立单向数据流。
// 子组件
@Component
struct ProgressBar {
@Prop progress: number
@Prop color: Color = Color.Blue
build() {
Column() {
Text(`进度: ${this.progress}%`)
.fontSize(16)
.margin({ bottom: 8 })
Stack() {
// 背景条
Rectangle()
.width('100%')
.height(8)
.fill('#E5E5E5')
.borderRadius(4)
// 进度条
Rectangle()
.width(`${this.progress}%`)
.height(8)
.fill(this.color)
.borderRadius(4)
}
.width('100%')
.height(8)
}
.width('100%')
}
}
// 父组件
@Entry
@Component
struct ParentComponent {
@State currentProgress: number = 30
build() {
Column({ space: 30 }) {
Text('下载进度演示')
.fontSize(24)
.fontWeight(FontWeight.Bold)
ProgressBar({ progress: this.currentProgress })
Slider({
value: this.currentProgress,
min: 0,
max: 100,
step: 1,
style: SliderStyle.OutSet
})
.width('80%')
.onChange((value: number) => {
this.currentProgress = value
})
Button('随机进度')
.onClick(() => {
this.currentProgress = Math.floor(Math.random() * 100)
})
.width(200)
}
.width('100%')
.height('100%')
.padding(20)
}
}
@Prop的特性:
- 单向数据流:父组件变化影响子组件,但子组件修改不影响父组件
- 本地修改不会被父组件覆盖
- 适合展示型组件
2.3 @Link:父子组件双向同步
@Link建立父子组件之间的双向数据绑定。
// 颜色选择器子组件
@Component
struct ColorPicker {
@Link selectedColor: Color
private colors: Color[] = [
Color.Red, Color.Blue, Color.Green,
Color.Yellow, Color.Orange, Color.Purple
]
build() {
Column() {
Text('选择主题颜色')
.fontSize(18)
.margin({ bottom: 16 })
Wrap({ spacing: 10 }) {
ForEach(this.colors, (color, index) => {
Circle({ width: 40, height: 40 })
.fill(color)
.stroke(this.selectedColor === color ? '#333' : '#CCC')
.strokeWidth(this.selectedColor === color ? 3 : 1)
.onClick(() => {
this.selectedColor = color
})
})
}
.width('100%')
}
}
}
// 父组件
@Entry
@Component
struct ThemeSettings {
@State themeColor: Color = Color.Blue
build() {
Column({ space: 20 }) {
Text('主题设置')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor(this.themeColor)
Rectangle()
.width('90%')
.height(120)
.fill(this.themeColor)
.borderRadius(12)
.shadow({ radius: 8, color: '#40000000', offsetX: 0, offsetY: 4 })
ColorPicker({ selectedColor: $themeColor })
Text('当前颜色值: ' + this.themeColor)
.fontSize(14)
.fontColor('#666')
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor('#F8F8F8')
}
}
@Link的特性:
- 双向数据绑定:任何一方修改都会同步到另一方
- 使用
$符号传递引用 - 适合表单控件和实时同步场景
三、跨组件状态共享
3.1 @Provide和@Consume:跨层级通信
@Provide和@Consume实现祖先组件与后代组件之间的直接通信,避免层层传递。
// 用户上下文提供者
@Entry
@Component
struct UserProvider {
@Provide username: string = '鸿蒙开发者'
@Provide userLevel: number = 1
@Provide isVIP: boolean = false
build() {
Column() {
Text('用户信息管理')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 30 })
UserProfile()
Divider()
.margin({ top: 30, bottom: 20 })
UpgradePanel()
}
.width('100%')
.height('100%')
.padding(20)
}
}
// 用户信息显示组件
@Component
struct UserProfile {
@Consume username: string
@Consume userLevel: number
@Consume isVIP: boolean
build() {
Column() {
Row() {
Image($r('app.media.user_avatar'))
.width(60)
.height(60)
.borderRadius(30)
.margin({ right: 15 })
Column() {
Text(this.username)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`等级: ${this.userLevel}`)
.fontSize(14)
.fontColor('#666')
if (this.isVIP) {
Text('VIP会员')
.fontSize(12)
.fontColor('#FF6B00')
.backgroundColor('#FFF0E6')
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
.borderRadius(4)
}
}
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.justifyContent(FlexAlign.Start)
}
}
}
// 升级面板组件
@Component
struct UpgradePanel {
@Consume username: string
@Consume userLevel: number
@Consume isVIP: boolean
build() {
Column() {
Text('账户升级')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 15 })
Button(this.isVIP ? '续费VIP' : '开通VIP')
.width(200)
.backgroundColor(this.isVIP ? '#FF6B00' : '#007DFF')
.onClick(() => {
// VIP状态切换逻辑
})
if (!this.isVIP) {
Text('开通VIP享受更多特权')
.fontSize(12)
.fontColor('#999')
.margin({ top: 8 })
}
}
.width('100%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 2, color: '#10000000', offsetX: 0, offsetY: 1 })
}
}
3.2 @Observed和@ObjectLink:复杂对象观察
对于嵌套对象,需要使用@Observed和@ObjectLink来观察内部属性变化。
// 定义可观察的类
@Observed
class UserSettings {
theme: string = 'light'
fontSize: number = 16
notifications: boolean = true
constructor(theme: string, fontSize: number, notifications: boolean) {
this.theme = theme
this.fontSize = fontSize
this.notifications = notifications
}
}
@Entry
@Component
struct SettingsApp {
@State settings: UserSettings = new UserSettings('light', 16, true)
build() {
Column() {
Text('应用设置')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 30 })
SettingsEditor({ settings: this.settings })
PreviewPanel({ settings: this.settings })
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor(this.settings.theme === 'light' ? '#FFFFFF' : '#1C1C1E')
}
}
@Component
struct SettingsEditor {
@ObjectLink settings: UserSettings
build() {
Column({ space: 20 }) {
// 主题选择
Row() {
Text('主题模式')
.fontSize(16)
.layoutWeight(1)
Toggle({ type: ToggleType.Checkbox, isOn: this.settings.theme === 'dark' })
.onChange((isOn: boolean) => {
this.settings = new UserSettings(
isOn ? 'dark' : 'light',
this.settings.fontSize,
this.settings.notifications
)
})
}
.width('100%')
// 字体大小调整
Row() {
Text('字体大小')
.fontSize(16)
.layoutWeight(1)
Text(`${this.settings.fontSize}px`)
.fontSize(14)
.fontColor('#666')
Button('-')
.onClick(() => {
if (this.settings.fontSize > 12) {
this.settings = new UserSettings(
this.settings.theme,
this.settings.fontSize - 2,
this.settings.notifications
)
}
})
Button('+')
.onClick(() => {
if (this.settings.fontSize < 24) {
this.settings = new UserSettings(
this.settings.theme,
this.settings.fontSize + 2,
this.settings.notifications
)
}
})
}
.width('100%')
}
.width('100%')
.padding(20)
.backgroundColor(this.settings.theme === 'light' ? '#F5F5F5' : '#2C2C2E')
.borderRadius(12)
}
}
四、应用级状态管理
4.1 AppStorage:全局状态管理
AppStorage提供应用级别的全局状态管理。
// 初始化全局状态
AppStorage.SetOrCreate('globalTheme', 'light')
AppStorage.SetOrCreate('language', 'zh-CN')
AppStorage.SetOrCreate('isLoggedIn', false)
@Entry
@Component
struct GlobalStateApp {
@StorageLink('globalTheme') currentTheme: string = 'light'
@StorageLink('language') currentLanguage: string = 'zh-CN'
@StorageLink('isLoggedIn') isLoggedIn: boolean = false
build() {
Column() {
Text('全局状态管理')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 30 })
// 主题切换
Row() {
Text('当前主题: ')
Text(this.currentTheme)
.fontColor(this.currentTheme === 'light' ? '#333' : '#FFF')
}
.margin({ bottom: 20 })
Button('切换主题')
.onClick(() => {
this.currentTheme = this.currentTheme === 'light' ? 'dark' : 'light'
})
.width(200)
.margin({ bottom: 30 })
// 多语言设置
Row() {
Text('当前语言: ')
Text(this.currentLanguage)
}
.margin({ bottom: 20 })
Picker({ selected: this.currentLanguage, range: ['zh-CN', 'en-US', 'ja-JP'] })
.onChange((value: string) => {
this.currentLanguage = value
})
.width(200)
.margin({ bottom: 30 })
// 登录状态
Toggle({ type: ToggleType.Checkbox, isOn: this.isLoggedIn })
.onChange((isOn: boolean) => {
this.isLoggedIn = isOn
})
Text(this.isLoggedIn ? '已登录' : '未登录')
.fontSize(14)
.fontColor(this.isLoggedIn ? '#52C41A' : '#FF4D4F')
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor(this.currentTheme === 'light' ? '#FFFFFF' : '#1C1C1E')
}
}
4.2 PersistentStorage:数据持久化
PersistentStorage将AppStorage中的数据持久化到设备本地。
// 在应用启动时初始化持久化存储
PersistentStorage.PersistProp('userPreferences', {
theme: 'light',
language: 'zh-CN',
fontSize: 16,
notifications: true
})
PersistentStorage.PersistProp('appSettings', {
autoSave: true,
cloudSync: false,
privacyMode: true
})
@Entry
@Component
struct PersistentApp {
@StorageLink('userPreferences') preferences: any = {}
@StorageLink('appSettings') settings: any = {}
aboutToAppear() {
// 应用启动时,持久化的数据会自动加载
console.log('用户偏好设置:', this.preferences)
console.log('应用设置:', this.settings)
}
build() {
Column({ space: 20 }) {
Text('数据持久化演示')
.fontSize(24)
.fontWeight(FontWeight.Bold)
// 用户偏好设置
Column() {
Text('用户偏好设置')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 15 })
Row() {
Text(`主题: ${this.preferences.theme}`)
Text(`语言: ${this.preferences.language}`)
Text(`字体: ${this.preferences.fontSize}px`)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.padding(15)
.backgroundColor(Color.White)
.borderRadius(12)
// 应用设置
Column() {
Text('应用设置')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 15 })
Row() {
Text(`自动保存: ${this.settings.autoSave ? '开启' : '关闭'}`)
Text(`云同步: ${this.settings.cloudSync ? '开启' : '关闭'}`)
Text(`隐私模式: ${this.settings.privacyMode ? '开启' : '关闭'}`)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.padding(15)
.backgroundColor(Color.White)
.borderRadius(12)
Button('修改设置')
.onClick(() => {
// 修改设置会自动持久化
this.preferences = {
...this.preferences,
theme: this.preferences.theme === 'light' ? 'dark' : 'light',
fontSize: this.preferences.fontSize + 2
}
})
.width(200)
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor('#F5F5F5')
}
}
五、状态管理最佳实践
5.1 状态管理方案选择指南
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 组件内部私有状态 | @State | 简单直接,响应式更新组件UI |
| 父子组件简单同步 | @Link(双向)/ @Prop(单向) | 关系明确,数据流清晰 |
| 跨多层组件传递 | @Provide / @Consume | 避免"prop drilling",简化代码 |
| 全局UI状态(如主题) | AppStorage + @StorageLink | 一处修改,全局响应 |
| 需要持久化的全局配置 | PersistentStorage + AppStorage | 数据持久化,应用重启不丢失 |
5.2 性能优化建议
- 避免不必要的渲染
// 错误做法:直接修改对象属性
this.user.profile.name = '新名字' // 不会触发UI更新
// 正确做法:创建新对象
this.user = {
...this.user,
profile: {
...this.user.profile,
name: '新名字'
}
}
- 精细化状态划分
// 不推荐:一个大状态对象
@State userData: {
profile: any,
settings: any,
preferences: any
} = {...}
// 推荐:拆分为多个小状态
@State userProfile: any = {...}
@State userSettings: any = {...}
@State userPreferences: any = {...}
- 合理使用@Watch监听器
@Component
struct SmartComponent {
@State @Watch('onCountChange') count: number = 0
@State doubledCount: number = 0
onCountChange() {
// 只有count变化时才执行计算
this.doubledCount = this.count * 2
}
build() {
// UI代码
}
}
六、总结
通过本篇教程,您已经掌握了:
✅ 状态管理的基本概念和核心原理
✅ 组件级状态管理(@State、@Prop、@Link)的使用
✅ 跨组件状态共享(@Provide/@Consume、@Observed/@ObjectLink)
✅ 应用级状态管理(AppStorage、PersistentStorage)
✅ 性能优化和最佳实践
关键知识点回顾:
- 状态驱动UI更新是声明式开发的核心
- 根据数据流方向选择合适的装饰器
- 复杂对象需要使用@Observed和@ObjectLink
- 全局状态使用AppStorage,持久化使用PersistentStorage
下一篇我们将学习页面路由与导航,掌握多页面应用的开发技巧。建议您动手实践本文中的各种状态管理场景,特别是综合案例部分,这将帮助您深入理解状态管理的各种模式和使用场景。
浙公网安备 33010602011771号