HarmonyOS状态管理入门:@State与@Link装饰器的原理与实践

状态管理是声明式UI开发的核心概念。本文将深入讲解HarmonyOS中最基础且最重要的两个状态装饰器:@State和@Link,帮助你掌握组件内状态管理和父子组件状态同步的核心技能。

一、状态管理基础概念

1.1 什么是状态管理

@Component
struct StateBasicConcept {
  // 普通变量 - 不会触发UI更新
  normalCount: number = 0
  
  // @State装饰的变量 - 变化时自动更新UI
  @State stateCount: number = 0

  build() {
    Column({ space: 20 }) {
      Text('状态管理基础概念')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      // 普通变量演示
      Text(`普通变量: ${this.normalCount}`)
        .fontSize(16)
        .fontColor('#666')
      
      // @State变量演示
      Text(`@State变量: ${this.stateCount}`)
        .fontSize(16)
        .fontColor('#007DFF')
        .fontWeight(FontWeight.Bold)
      
      Row({ space: 15 }) {
        Button('普通变量+1')
          .onClick(() => {
            this.normalCount++
            console.log('普通变量值:', this.normalCount) // 值会变化,但UI不会更新
          })
        
        Button('@State变量+1')
          .onClick(() => {
            this.stateCount++ // 值变化,UI自动更新
          })
      }
      
      // 状态变化监听
      Text('状态变化日志:')
        .fontSize(14)
        .fontColor('#333')
        .margin({ top: 20 })
    }
    .width('100%')
    .padding(20)
  }
}

1.2 状态管理的核心原理

@Component
struct StatePrincipleDemo {
  @State private userInfo: User = {
    name: '张三',
    age: 25,
    isVIP: false
  }

  build() {
    Column({ space: 15 }) {
      Text('状态管理原理演示')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
      
      // 对象状态演示
      Text(`用户名: ${this.userInfo.name}`)
        .fontSize(16)
      Text(`年龄: ${this.userInfo.age}`)
        .fontSize(16)
      Text(`VIP状态: ${this.userInfo.isVIP ? '是' : '否'}`)
        .fontSize(16)
        .fontColor(this.userInfo.isVIP ? '#FF9500' : '#666')
      
      Divider()
      
      Text('状态更新操作:').fontSize(14).fontColor('#333')
      
      Row({ space: 10 }) {
        Button('修改名字')
          .onClick(() => {
            // 错误方式:直接修改属性不会触发更新
            // this.userInfo.name = '李四'
            
            // 正确方式:创建新对象
            this.userInfo = {
              ...this.userInfo,
              name: '李四'
            }
          })
        
        Button('增加年龄')
          .onClick(() => {
            this.userInfo = {
              ...this.userInfo,
              age: this.userInfo.age + 1
            }
          })
        
        Button('切换VIP')
          .onClick(() => {
            this.userInfo = {
              ...this.userInfo,
              isVIP: !this.userInfo.isVIP
            }
          })
      }
    }
    .width('100%')
    .padding(20)
  }
}

class User {
  name: string = ''
  age: number = 0
  isVIP: boolean = false
}

二、@State装饰器深度解析

2.1 @State基础用法

@Component
struct StateBasicUsage {
  // 基本类型状态
  @State count: number = 0
  @State message: string = 'Hello'
  @State isActive: boolean = false
  
  // 数组类型状态
  @State numberList: number[] = [1, 2, 3]
  @State stringList: string[] = ['A', 'B', 'C']
  
  // 对象类型状态
  @State config: Config = {
    theme: 'light',
    fontSize: 16,
    language: 'zh-CN'
  }

  build() {
    Column({ space: 20 }) {
      Text('@State基础用法')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      // 基本类型状态展示
      this.buildBasicStateDemo()
      
      // 数组类型状态展示
      this.buildArrayStateDemo()
      
      // 对象类型状态展示
      this.buildObjectStateDemo()
    }
    .width('100%')
    .padding(20)
  }

  @Builder
  buildBasicStateDemo() {
    Column({ space: 10 }) {
      Text('基本类型状态').fontSize(16).fontWeight(FontWeight.Medium)
      
      Row({ space: 15 }) {
        Text(`计数: ${this.count}`).fontSize(14)
        Text(`消息: ${this.message}`).fontSize(14)
        Text(`活跃: ${this.isActive}`).fontSize(14)
      }
      
      Row({ space: 10 }) {
        Button('计数+')
          .onClick(() => this.count++)
        Button('改消息')
          .onClick(() => this.message = 'Hello HarmonyOS')
        Button('切换状态')
          .onClick(() => this.isActive = !this.isActive)
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#F8F9FA')
    .borderRadius(8)
  }

  @Builder
  buildArrayStateDemo() {
    Column({ space: 10 }) {
      Text('数组类型状态').fontSize(16).fontWeight(FontWeight.Medium)
      
      Text(`数组: [${this.numberList.join(', ')}]`).fontSize(14)
      
      Row({ space: 10 }) {
        Button('添加元素')
          .onClick(() => {
            this.numberList = [...this.numberList, this.numberList.length + 1]
          })
        Button('删除元素')
          .onClick(() => {
            if (this.numberList.length > 0) {
              this.numberList = this.numberList.slice(0, -1)
            }
          })
        Button('清空数组')
          .onClick(() => {
            this.numberList = []
          })
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#F8F9FA')
    .borderRadius(8)
  }

  @Builder
  buildObjectStateDemo() {
    Column({ space: 10 }) {
      Text('对象类型状态').fontSize(16).fontWeight(FontWeight.Medium)
      
      Text(`主题: ${this.config.theme}`).fontSize(14)
      Text(`字体: ${this.config.fontSize}`).fontSize(14)
      Text(`语言: ${this.config.language}`).fontSize(14)
      
      Row({ space: 10 }) {
        Button('切换主题')
          .onClick(() => {
            this.config = {
              ...this.config,
              theme: this.config.theme === 'light' ? 'dark' : 'light'
            }
          })
        Button('增大字体')
          .onClick(() => {
            this.config = {
              ...this.config,
              fontSize: this.config.fontSize + 2
            }
          })
        Button('切换语言')
          .onClick(() => {
            this.config = {
              ...this.config,
              language: this.config.language === 'zh-CN' ? 'en-US' : 'zh-CN'
            }
          })
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#F8F9FA')
    .borderRadius(8)
  }
}

class Config {
  theme: string = 'light'
  fontSize: number = 16
  language: string = 'zh-CN'
}

2.2 @State高级特性

@Component
struct StateAdvancedFeatures {
  @State @Watch('onCountChange') count: number = 0
  @State @Watch('onUserChange') user: User = new User()

  // 状态变化监听
  onCountChange(): void {
    console.log('count发生变化:', this.count)
  }

  onUserChange(): void {
    console.log('user发生变化:', JSON.stringify(this.user))
  }

  build() {
    Column({ space: 20 }) {
      Text('@State高级特性')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Text(`计数: ${this.count}`).fontSize(16)
      Text(`用户名: ${this.user.name}`).fontSize(16)
      Text(`年龄: ${this.user.age}`).fontSize(16)
      
      Row({ space: 10 }) {
        Button('计数+')
          .onClick(() => this.count++)
        Button('修改用户')
          .onClick(() => {
            this.user = new User()
            this.user.name = '李四'
            this.user.age = 30
          })
      }
      
      // 状态依赖演示
      this.buildComputedState()
    }
    .width('100%')
    .padding(20)
  }

  @Builder
  buildComputedState() {
    // 计算属性(基于其他状态)
    const doubleCount: number = this.count * 2
    const isAdult: boolean = this.user.age >= 18
    const userStatus: string = `${this.user.name} (${isAdult ? '成年' : '未成年'})`

    Column({ space: 10 }) {
      Text('计算属性演示').fontSize(16).fontWeight(FontWeight.Medium)
      
      Text(`双倍计数: ${doubleCount}`).fontSize(14)
      Text(`用户状态: ${userStatus}`).fontSize(14)
      Text(`是否成年: ${isAdult ? '是' : '否'}`)
        .fontSize(14)
        .fontColor(isAdult ? '#34C759' : '#FF9500')
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#F0F8FF')
    .borderRadius(8)
  }
}

三、@Link装饰器深度解析

3.1 @Link基础用法

@Component
struct ParentComponent {
  @State parentCount: number = 0
  @State parentMessage: string = '父组件消息'

  build() {
    Column({ space: 20 }) {
      Text('父组件')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Text(`父组件计数: ${this.parentCount}`).fontSize(16)
      Text(`父组件消息: ${this.parentMessage}`).fontSize(16)
      
      Row({ space: 10 }) {
        Button('父组件+1')
          .onClick(() => this.parentCount++)
        Button('修改消息')
          .onClick(() => this.parentMessage = '消息已修改')
      }
      
      Divider().margin({ top: 10, bottom: 10 })
      
      // 子组件 - 使用@Link建立双向绑定
      ChildComponent({
        childCount: $parentCount,  // 双向绑定
        childMessage: this.parentMessage  // 单向传递
      })
    }
    .width('100%')
    .padding(20)
    .backgroundColor('#E3F2FD')
    .borderRadius(12)
  }
}

@Component
struct ChildComponent {
  @Link childCount: number  // 与父组件双向绑定
  @Prop childMessage: string  // 从父组件单向接收
  
  @State localCount: number = 0  // 子组件本地状态

  build() {
    Column({ space: 15 }) {
      Text('子组件')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
      
      Text(`@Link计数: ${this.childCount}`)
        .fontSize(14)
        .fontColor('#007DFF')
      
      Text(`@Prop消息: ${this.childMessage}`)
        .fontSize(14)
        .fontColor('#666')
      
      Text(`本地计数: ${this.localCount}`)
        .fontSize(14)
        .fontColor('#999')
      
      Row({ space: 10 }) {
        Button('@Link+1')
          .onClick(() => {
            this.childCount++  // 会同步到父组件
          })
        
        Button('本地+1')
          .onClick(() => {
            this.localCount++  // 只影响子组件
          })
      }
      
      Text('说明: @Link修改会影响父组件,@Prop只能读取')
        .fontSize(12)
        .fontColor('#FF6B6B')
    }
    .width('90%')
    .padding(15)
    .backgroundColor('#FFFFFF')
    .borderRadius(8)
    .border({ width: 2, color: '#007DFF' })
  }
}

3.2 @Link高级应用

@Component
struct ComplexParentComponent {
  @State userList: User[] = [
    new User('张三', 25),
    new User('李四', 30),
    new User('王五', 28)
  ]
  
  @State selectedUserIndex: number = 0

  build() {
    Column({ space: 20 }) {
      Text('复杂数据@Link演示')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Text('用户列表:').fontSize(16).fontWeight(FontWeight.Medium)
      
      // 用户列表展示
      ForEach(this.userList, (user: User, index: number) => {
        Row({ space: 10 }) {
          Text(`${index + 1}. ${user.name} - ${user.age}岁`)
            .fontSize(14)
            .layoutWeight(1)
          
          Button('选择')
            .onClick(() => this.selectedUserIndex = index)
            .stateEffect(this.selectedUserIndex === index)
        }
        .width('100%')
        .padding(10)
        .backgroundColor(this.selectedUserIndex === index ? '#E3F2FD' : '#F5F5F5')
        .borderRadius(6)
      })
      
      Divider()
      
      // 用户编辑组件
      if (this.selectedUserIndex >= 0) {
        UserEditorComponent({
          user: $userList[this.selectedUserIndex]  // 双向绑定数组中的对象
        })
      }
    }
    .width('100%')
    .padding(20)
  }
}

@Component
struct UserEditorComponent {
  @Link user: User
  
  @State localEdit: boolean = false
  @State tempName: string = ''
  @State tempAge: number = 0

  build() {
    Column({ space: 15 }) {
      Text('用户编辑器')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
      
      if (!this.localEdit) {
        // 显示模式
        Text(`当前用户: ${this.user.name}`).fontSize(14)
        Text(`年龄: ${this.user.age}`).fontSize(14)
        
        Button('编辑模式')
          .onClick(() => {
            this.tempName = this.user.name
            this.tempAge = this.user.age
            this.localEdit = true
          })
      } else {
        // 编辑模式
        TextInput({ placeholder: '输入姓名', text: this.tempName })
          .onChange((value: string) => {
            this.tempName = value
          })
        
        TextInput({ placeholder: '输入年龄', text: this.tempAge.toString() })
          .onChange((value: string) => {
            this.tempAge = parseInt(value) || 0
          })
        
        Row({ space: 10 }) {
          Button('保存')
            .onClick(() => {
              this.user.name = this.tempName
              this.user.age = this.tempAge
              this.localEdit = false
            })
          
          Button('取消')
            .onClick(() => {
              this.localEdit = false
            })
        }
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#F8F9FA')
    .borderRadius(8)
  }
}

四、@State与@Link对比实践

4.1 综合对比演示

@Component
struct StateVsLinkDemo {
  @State globalCount: number = 0
  @State userData: UserData = new UserData()

  build() {
    Column({ space: 25 }) {
      Text('@State vs @Link 综合对比')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .fontColor('#007DFF')
      
      // 全局状态展示
      this.buildGlobalState()
      
      // 组件对比演示
      Row({ space: 20 }) {
        // @State管理的子组件
        StateManagedComponent({ 
          localCount: this.globalCount 
        })
        
        // @Link管理的子组件
        LinkManagedComponent({ 
          linkedCount: $globalCount 
        })
      }
      .width('100%')
      .height(400)
      
      // 复杂对象状态演示
      this.buildObjectStateDemo()
    }
    .width('100%')
    .padding(20)
    .backgroundColor('#FFFFFF')
  }

  @Builder
  buildGlobalState() {
    Column({ space: 10 }) {
      Text('全局状态监控').fontSize(16).fontWeight(FontWeight.Medium)
      
      Row({ space: 20 }) {
        Column({ space: 5 }) {
          Text(`计数: ${this.globalCount}`).fontSize(14)
          Progress({ value: this.globalCount, total: 100 })
            .width(100)
            .color('#007DFF')
        }
        
        Button('重置计数')
          .onClick(() => this.globalCount = 0)
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#F0F8FF')
    .borderRadius(8)
  }

  @Builder
  buildObjectStateDemo() {
    Column({ space: 15 }) {
      Text('对象状态管理').fontSize(16).fontWeight(FontWeight.Medium)
      
      ObjectStateManager({
        userData: $userData
      })
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#FFF8E1')
    .borderRadius(8)
  }
}

@Component
struct StateManagedComponent {
  @Prop localCount: number
  @State internalCount: number = 0

  build() {
    Column({ space: 15 }) {
      Text('@State管理组件')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FF6B6B')
      
      Text('特性:')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
      
      Text('• 组件内部状态管理')
        .fontSize(12)
      Text('• 状态变化触发UI更新')
        .fontSize(12)
      Text('• 不直接影响父组件')
        .fontSize(12)
      
      Divider().margin({ top: 10, bottom: 10 })
      
      Text(`接收的Prop: ${this.localCount}`)
        .fontSize(14)
        .fontColor('#666')
      
      Text(`内部状态: ${this.internalCount}`)
        .fontSize(14)
        .fontColor('#007DFF')
      
      Row({ space: 10 }) {
        Button('内部+1')
          .onClick(() => this.internalCount++)
        Button('内部×2')
          .onClick(() => this.internalCount *= 2)
      }
    }
    .width('100%')
    .height('100%')
    .padding(15)
    .backgroundColor('#FFEBEE')
    .borderRadius(8)
    .border({ width: 2, color: '#FF6B6B' })
  }
}

@Component
struct LinkManagedComponent {
  @Link linkedCount: number
  @State operationHistory: string[] = []

  build() {
    Column({ space: 15 }) {
      Text('@Link管理组件')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#4ECDC4')
      
      Text('特性:')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
      
      Text('• 与父组件双向绑定')
        .fontSize(12)
      Text('• 修改会影响父组件')
        .fontSize(12)
      Text('• 适合状态共享场景')
        .fontSize(12)
      
      Divider().margin({ top: 10, bottom: 10 })
      
      Text(`链接的计数: ${this.linkedCount}`)
        .fontSize(14)
        .fontColor('#007DFF')
      
      Row({ space: 10 }) {
        Button('链接+1')
          .onClick(() => {
            this.linkedCount++
            this.addHistory('链接计数+1')
          })
        Button('链接×2')
          .onClick(() => {
            this.linkedCount *= 2
            this.addHistory('链接计数×2')
          })
        Button('链接重置')
          .onClick(() => {
            this.linkedCount = 0
            this.addHistory('链接计数重置')
          })
      }
      
      // 操作历史
      if (this.operationHistory.length > 0) {
        Column({ space: 5 }) {
          Text('操作历史:').fontSize(12).fontColor('#666')
          ForEach(this.operationHistory, (history: string) => {
            Text(history).fontSize(10).fontColor('#999')
          })
        }
        .margin({ top: 10 })
      }
    }
    .width('100%')
    .height('100%')
    .padding(15)
    .backgroundColor('#E0F7FA')
    .borderRadius(8)
    .border({ width: 2, color: '#4ECDC4' })
  }

  private addHistory(operation: string): void {
    this.operationHistory = [
      `${new Date().toLocaleTimeString()}: ${operation}`,
      ...this.operationHistory.slice(0, 4) // 只保留最近5条
    ]
  }
}

@Component
struct ObjectStateManager {
  @Link userData: UserData

  build() {
    Column({ space: 10 }) {
      Text('用户数据管理器').fontSize(14).fontWeight(FontWeight.Medium)
      
      Text(`姓名: ${this.userData.name}`).fontSize(12)
      Text(`年龄: ${this.userData.age}`).fontSize(12)
      Text(`积分: ${this.userData.points}`).fontSize(12)
      
      Row({ space: 5 }) {
        Button('改名字')
          .onClick(() => {
            this.userData.name = this.userData.name === '张三' ? '李四' : '张三'
          })
        Button('加年龄')
          .onClick(() => this.userData.age++)
        Button('加积分')
          .onClick(() => this.userData.points += 10)
      }
    }
    .width('100%')
  }
}

class UserData {
  name: string = '张三'
  age: number = 25
  points: number = 100
}

五、最佳实践与常见问题

5.1 状态管理最佳实践

@Component
struct StateBestPractices {
  @State private user: User = new User()
  @State private loading: boolean = false
  @State private error: string = ''

  build() {
    Column({ space: 20 }) {
      Text('状态管理最佳实践')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      // 错误处理状态
      if (this.error) {
        this.buildErrorState()
      }
      
      // 加载状态
      if (this.loading) {
        this.buildLoadingState()
      } else {
        this.buildContent()
      }
      
      // 操作按钮
      this.buildActionButtons()
    }
    .width('100%')
    .padding(20)
  }

  @Builder
  buildErrorState() {
    Row({ space: 10 }) {
      Image($r('app.media.error_icon'))
        .width(20)
        .height(20)
      Text(this.error)
        .fontSize(14)
        .fontColor('#FF3B30')
    }
    .width('100%')
    .padding(10)
    .backgroundColor('#FFEBEE')
    .borderRadius(6)
  }

  @Builder
  buildLoadingState() {
    Column({ space: 10 }) {
      Progress({ value: 50, total: 100 })
        .width(200)
      Text('加载中...')
        .fontSize(14)
        .fontColor('#666')
    }
    .width('100%')
    .padding(20)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildContent() {
    Column({ space: 15 }) {
      Text(`用户: ${this.user.name}`).fontSize(16)
      Text(`年龄: ${this.user.age}`).fontSize(16)
      Text(`VIP: ${this.user.isVIP ? '是' : '否'}`).fontSize(16)
    }
  }

  @Builder
  buildActionButtons() {
    Column({ space: 10 }) {
      Row({ space: 10 }) {
        Button('模拟加载')
          .onClick(() => this.simulateLoad())
        Button('模拟错误')
          .onClick(() => this.simulateError())
        Button('重置状态')
          .onClick(() => this.resetState())
      }
      
      Text('最佳实践提示:')
        .fontSize(12)
        .fontColor('#666')
        .margin({ top: 10 })
      
      Text('1. 使用不可变数据更新状态')
        .fontSize(10)
      Text('2. 合理使用@Watch监听状态变化')
        .fontSize(10)
      Text('3. 避免在build()中修改状态')
        .fontSize(10)
      Text('4. 使用条件渲染处理不同状态')
        .fontSize(10)
    }
  }

  private simulateLoad(): void {
    this.loading = true
    this.error = ''
    
    // 模拟异步加载
    setTimeout(() => {
      this.loading = false
      this.user = {
        name: '加载的用户',
        age: 30,
        isVIP: true
      }
    }, 2000)
  }

  private simulateError(): void {
    this.loading = true
    setTimeout(() => {
      this.loading = false
      this.error = '模拟错误:数据加载失败'
    }, 1000)
  }

  private resetState(): void {
    this.user = new User()
    this.loading = false
    this.error = ''
  }
}

总结

通过本文的深入学习,你应该已经掌握了:

  1. @State装饰器:组件内部状态管理,状态变化自动更新UI
  2. @Link装饰器:父子组件双向状态绑定,实现状态共享
  3. 状态管理原则:不可变数据、合理监听、条件渲染等最佳实践
  4. 实战应用:各种数据类型的状态管理和组件通信

@State和@Link是HarmonyOS状态管理的基础,熟练掌握它们将为后续学习更复杂的状态管理

需要参加鸿蒙认证的请点击 鸿蒙认证链接

posted @ 2025-10-30 10:54  ifeng918  阅读(14)  评论(0)    收藏  举报