Bootstrap

鸿蒙修饰符

文章目录

一、引言

在HarmonyOS(鸿蒙)应用开发中,修饰符(Decorator)是一个极其重要的概念。它们不仅能够简化开发过程,还能提供强大的功能扩展性。本文将深入探讨鸿蒙中的各类修饰符,帮助开发者更好地理解和使用这些工具。本文主要介绍@Styles@Extend@Builder@Component@BuilderParam@State@Prop@Link@Provide@Watch@Observed等等。

HarmonyOS Decorators
UI Decorators
Component Decorators
State Management
Styles
Extend
Component
BuilderParam
State
Prop
Link

1.1 什么是修饰符

修饰符是一种特殊的语法元素,用@符号作为标识,可以用来修饰类、方法、属性等代码元素,为其添加额外的行为或特性。在鸿蒙开发中,修饰符主要用于以下几个方面:

  • UI样式定义和复用
  • 组件声明和管理
  • 状态管理和数据同步
  • 组件间通信

1.2 修饰符在鸿蒙开发中的重要性

请添加图片描述

1.3 修饰符的作用机制

修饰符在编译时会被转换为特定的代码,它们主要通过以下机制发挥作用:

  1. 装饰器模式: 在不改变原有代码结构的情况下,动态地给对象添加新的功能
  2. 编译时转换: 将声明式的代码转换为命令式代码
  3. 运行时注入: 在运行时为目标对象注入额外的行为

二、UI装饰类修饰符

UI Decorator
Styles
Extend
Global Styles
Theme Styles
Component Extensions
Custom Components

2.1 @Styles修饰符

2.1.1 基本概念和使用场景

@Styles修饰符用于定义可复用的样式集合,类似于Web开发中的CSS类。它可以:

  • 提取共同样式,实现复用
  • 保持代码整洁
  • 统一管理样式

2.1.2 使用示例

// 定义全局样式
@Styles function globalStyles() {
  .width('100%')
  .height(100)
  .backgroundColor('#f5f5f5')
  .padding(20)
}

// 使用样式
@Entry
@Component
struct StylesDemo {
  build() {
    Column() {
      Text('Hello')
        .fontSize(20)
        .globalStyles()
      
      Text('World')
        .fontSize(16)
        .globalStyles()
    }
  }
}

2.1.3 最佳实践

样式分类管理

// 按功能分类样式
@Styles function cardStyles() {
  .width('100%')
  .padding(15)
  .borderRadius(8)
  .backgroundColor(Color.White)
}

@Styles function buttonStyles() {
  .width(120)
  .height(40)
  .borderRadius(20)
  .backgroundColor('#007DFF')
}

2.2 @Extend修饰符

2.2.1 基本概念

@Extend用于扩展现有组件的样式和行为,创建具有特定样式的新组件类型。

2.2.2 使用示例

// 扩展Text组件
@Extend(Text) function primaryText() {
    .fontSize(16)
    .fontColor('#333333')
    .fontWeight(FontWeight.Medium)
}

// 扩展Button组件
@Extend(Button) function primaryButton(color:Color) {
    .width(200)
   .height(50)
   .backgroundColor('#007DFF')
   .fontColor(color)
}

@Entry
@Component
struct TestCase {
   build() {
      Column({ space: 20 }) {
         Text('这是普通文本')
           .primaryText()

         Button('点击按钮')
          .primaryButton(Color.White)
      }
   .height('100%')
   .width('100%')
   }
}

2.2.3 @Extend vs @Styles 对比

特性@Extend@Styles
使用对象特定组件任意组件
复用性只能用于指定的组件类型可以应用于任何组件
类型安全更强的类型检查相对宽松
适用场景组件定制化通用样式复用
是否能接收参数

2.2.4 使用建议

  1. 选择合适的场景

    • 需要创建特定样式的组件变体时,使用@Extend
    • 需要跨组件复用样式时,使用@Styles
  2. 命名规范

// 好的命名示例
@Extend(Text) function primaryText() {}
@Extend(Text) function warningText() {}
@Extend(Button) function roundButton() {}

// 避免的命名方式
@Extend(Text) function txt() {}
@Extend(Button) function btn() {}

三、组件类修饰符

3.1 @Component修饰符

3.1.1 基本概念

@Component是鸿蒙开发中最核心的修饰符之一,用于声明自定义组件。它将一个struct转换为可重用的UI组件,具有以下特点:

  • 必须包含build()方法
  • 支持组件生命周期
  • 可以包含状态变量
  • 支持组件间通信
Component
生命周期管理
状态管理
UI渲染
aboutToAppear
aboutToDisappear
State
Prop
build方法

3.1.2 @Component vs 普通函数对比

特性@Component普通函数
状态管理支持@State等状态修饰符不支持状态管理
生命周期有完整生命周期没有生命周期
重渲染机制响应式更新需手动处理更新
使用场景复杂UI组件简单功能封装

3.1.3 基础使用示例

import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct TestCase {
   // 组件私有状态
@State counter: number = 0
   // 生命周期函数
   aboutToAppear() {
      promptAction.showToast({message:'Component is about to appear'})
   }
   aboutToDisappear() {
      promptAction.showToast({message:'Component is about to disappear'})
   }

   build() {
      Column() {
         Text(`计数器: ${this.counter}`)
           .fontSize(20)
           .fontWeight(FontWeight.Bold)

         Button('增加')
           .onClick(() => {
              this.counter++
           })
           .margin(10)
      }
   .width('100%')
   .padding(20)
   }
}

3.1.4 高级特性

1. 组件参数传递

@Component
struct AdvancedComponent {
  // 从父组件接收的属性
  @Prop title: string
  @State private description: string = ''

  build() {
    Column({ space: 10 }) {
      Text(this.title)
        .fontSize(24)
      
      TextInput({ placeholder: '请输入描述' })
        .onChange((value: string) => {
          this.description = value
        })
    }
  }
}

2. 组件插槽(Slot)

@Component
struct SlotComponent {
  @BuilderParam content: () => void

  build() {
    Column() {
      this.content()
    }
    .width('100%')
    .backgroundColor('#f5f5f5')
  }
}

// 使用组件插槽
@Component
struct ParentComponent {
  build() {
    SlotComponent() {
      Row() {
        Text('自定义内容')
        Image('icon.png')
      }
    }
  }
}

3.1.5 最佳实践

  1. 组件拆分原则
// 好的实践:功能单一,职责明确
@Component
struct UserAvatar {
  @Prop userName: string
  @Prop avatarUrl: string

  build() {
    Stack() {
      Image(this.avatarUrl)
        .width(50)
        .height(50)
        .borderRadius(25)
      
      Text(this.userName[0])
        .fontSize(20)
        .fontColor(Color.White)
    }
  }
}

// 使用组件
@Component
struct UserProfile {
  build() {
    Row() {
      UserAvatar({
        userName: "张三",
        avatarUrl: "avatar.png"
      })
      Text("其他信息")
    }
  }
}
  1. 生命周期使用建议
@Component
struct LifecycleComponent {
  @State private data: any = {}

  aboutToAppear() {
    // 初始化数据,订阅事件
    this.initData()
  }

  aboutToDisappear() {
    // 清理资源,取消订阅
    this.cleanUp()
  }

  private initData() {
    // 数据初始化逻辑
  }

  private cleanUp() {
    // 清理逻辑
  }

  build() {
    // UI渲染
  }
}

3.1.6 性能优化建议

  1. 状态管理优化
@Component
struct OptimizedComponent {
  // 使用私有变量存储不需要响应式更新的数据
  private staticData: string = 'static'
  
  // 只将需要触发更新的数据声明为@State
  @State dynamicData: number = 0

  build() {
    Column() {
      // 静态内容
      Text(this.staticData)
      
      // 动态内容
      Text(`${this.dynamicData}`)
    }
  }
}
  1. 条件渲染优化
@Component
struct ConditionalComponent {
  @State loading: boolean = true
  @State data: Array<string> = []

  build() {
    Column() {
      if (this.loading) {
        LoadingSpinner()
      } else {
        ForEach(this.data, (item) => {
          DataItem({ content: item })
        })
      }
    }
  }
}

3.2 @BuilderParam修饰符

3.2.1 基本概念

@BuilderParam用于声明自定义构建函数参数,使组件能够接收和渲染自定义内容。

3.2.2 使用场景

  1. 自定义列表项渲染

@Component
struct MainPage {
@State items: string[] = ['项目1', '项目2', '项目3']
@Builder myItemBuilder(item: string):void{}
@BuilderParam itemBuilder: (item: string) => void =  this.myItemBuilder
   build() {
      List() {
         ForEach(this.items, (item:string,idx:number) => {
            ListItem() {
               this.itemBuilder(item)
            }
         })
      }
   }
}

// 使用示例
@Entry
@Component
struct TestCase {
   build() {
      Column(){
         MainPage({
            itemBuilder: (item:string) => {
               Row() {
                  Text(item).width('100%')
                    .height('100%')
                    .backgroundColor(Color.Pink)
               }
            }
         })
      }}
}
  1. 可定制化容器组件
@Component
struct Container {
  @BuilderParam header?: () => void
  @BuilderParam content: () => void
  @BuilderParam footer?: () => void

  build() {
    Column() {
      if (this.header) {
        this.header()
      }
      
      this.content()
      
      if (this.footer) {
        this.footer()
      }
    }
  }
}

3.2.3 注意事项与最佳实践

  1. 参数命名规范
// 推荐的命名方式
@BuilderParam headerBuilder: () => void
@BuilderParam contentBuilder: () => void
@BuilderParam footerBuilder: () => void

// 不推荐的命名方式
@BuilderParam hdr: () => void
@BuilderParam cnt: () => void
  1. 性能考虑
@Component
struct PerformanceOptimizedList {
   // 使用私有变量缓存不常变化的构建函数

@Builder myStaticBuilder() :void{
      Text('Static Content')
}

@BuilderParam private staticBuilder:()=>void = this.myStaticBuilder

@BuilderParam dynamicBuilder: () => void

  build() {
      Column() {
         // 静态内容
         this.staticBuilder()

         // 动态内容
         this.dynamicBuilder()
      }
   }
}

四、状态管理类修饰符

4.1 @State修饰符

4.1.1 基本概念

@State是鸿蒙应用中最基础的状态管理修饰符,用于组件内部状态管理。当@State装饰的变量发生变化时,框架会自动重新渲染相关UI。

State变量改变
触发更新机制
计算差异
更新UI
完成渲染

4.1.2 使用规则

  1. 基本语法
interface IUser{
   name: string,
   age: number
}

// 使用示例
@Entry
@Component
struct TestCase {
   // 基础类型
   @State count: number = 0
   // 对象类型
   @State user: IUser  = {
      name: 'tata',
      age: 25
   }
      // 数组类型
   @State list: string[] = ['item1', 'item2']
   build() {
      Column({ space: 20 }) {
         // 使用状态变量
         Text(`Count: ${this.count}`)
         Button('Add')
           .onClick(() => {
              this.count++
           })

         Text(`User: ${this.user.name}, ${this.user.age}`)
         Button('Update User')
           .onClick(() => {
              // 对象更新要使用新对象
              this.user = {
                 name:this.user.name,
                 age: this.user.age + 1
              }
           })
      }
   }
}

4.1.3 @State注意事项和最佳实践

  1. 状态初始化
@Component
struct StateInitDemo {
  // ✅ 推荐:直接初始化
  @State counter1: number = 0
  
  // ⚠️ 不推荐:延迟初始化可能导致问题
  @State counter2: number

  aboutToAppear() {
    // 延迟初始化可能导致首次渲染问题
    this.counter2 = 0
  }
}
  1. 状态粒度控制
@Component
struct StateGranularityDemo {
  // ❌ 错误:粒度过大
  @State entirePage: {
    header: object,
    content: object,
    footer: object
  }

  // ✅ 正确:适当的状态粒度
  @State headerConfig: object
  @State contentList: array
  @State footerVisible: boolean

  build() {
    Column() {
      // UI实现
    }
  }
}

4.2 @Prop修饰符

4.2.1 基本概念

@Prop用于父子组件间的单向数据传递,子组件通过@Prop接收父组件传递的数据。

4.2.2 使用示例

// 子组件
@Component
struct ChildComponent {
  @Prop count: number  // 接收父组件传递的数值
  @Prop message: string  // 接收父组件传递的字符串

  build() {
    Column() {
      Text(`Count: ${this.count}`)
      Text(`Message: ${this.message}`)
    }
  }
}

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

  build() {
    Column() {
      // 传递属性给子组件
      ChildComponent({
        count: this.parentCount,
        message: this.parentMessage
      })

      Button('Update Parent')
        .onClick(() => {
          this.parentCount++
          this.parentMessage = 'Updated'
        })
    }
  }
}

4.2.3 @Prop vs 普通变量对比

@Component
struct PropComparisonDemo {
  // @Prop装饰的变量
  @Prop propValue: number
  
  // 普通变量
  normalValue: number

  build() {
    Column() {
      // @Prop变量会响应父组件的更新
      Text(`Prop Value: ${this.propValue}`)
      
      // 普通变量不会响应更新
      Text(`Normal Value: ${this.normalValue}`)
    }
  }
}
特性@Prop变量普通变量
数据流向单向(父到子)无数据流
响应更新自动响应不响应
可修改性本地可修改但不影响父组件可以自由修改
使用场景父子组件通信组件内部数据

4.2.4 @Prop的高级用法

  1. 默认值处理
@Component
struct PropDefaultDemo {
  // 使用装饰器提供默认值
  @Prop defaultValue: number = 100

  build() {
    Column() {
      Text(`Value: ${this.defaultValue}`)
    }
  }
}
  1. 属性监听与处理
@Component
struct PropWatchDemo {
  @Prop @Watch('onCountChange') count: number
  @State localCount: number = 0

  onCountChange() {
    // 当@Prop值变化时执行
    this.localCount = this.count * 2
  }

  build() {
    Column() {
      Text(`Prop Count: ${this.count}`)
      Text(`Local Count: ${this.localCount}`)
    }
  }
}

4.2.5 最佳实践

  1. 属性命名规范
@Component
struct PropNamingDemo {
  // ✅ 推荐:语义化命名
  @Prop isVisible: boolean
  @Prop userName: string
  @Prop itemCount: number

  // ❌ 避免:模糊的命名
  @Prop flag: boolean
  @Prop str: string
  @Prop num: number
}
  1. 类型安全
@Component
struct PropTypeDemo {
  // ✅ 推荐:明确的类型定义
  @Prop items: Array<{
    id: number,
    name: string
  }>

  // ❌ 避免:使用any类型
  @Prop data: any
}

4.3 @Link修饰符

4.3.1 基本概念

@Link修饰符用于实现父子组件间的双向数据绑定。与@Prop不同,@Link装饰的变量在子组件中的修改会同步回父组件。

父组件状态
Link双向绑定
子组件状态
父组件UI更新
子组件UI更新

4.3.2 使用示例

// 子组件
@Component
struct LinkChildComponent {
  @Link countValue: number  // 双向绑定数据

  build() {
    Column() {
      Text(`Count: ${this.countValue}`)
      Button('在子组件中修改')
        .onClick(() => {
          this.countValue++  // 修改会影响父组件
        })
    }
  }
}

// 父组件
@Entry
@Component
struct TestCase {
  @State parentCount: number = 0

  build() {
    Column() {
      Text(`Parent Count: ${this.parentCount}`)
      LinkChildComponent({ countValue: $parentCount })
      Button('在父组件中修改')
        .onClick(() => {
          this.parentCount++
        })
    }
  }
}

4.3.3 @Link vs @Prop 对比

特性@Link@Prop
数据流向双向单向(父到子)
子组件修改直接影响父组件不影响父组件
使用场景需要子组件修改父组件状态只需要展示父组件数据
语法标记变量名变量名

4.3.4 @Link最佳实践

  1. 合理使用场景
@Component
struct FormComponent {
  // ✅ 适合使用@Link的场景:表单数据
  @Link formData: {
    username: string,
    password: string
  }

  build() {
    Column() {
      TextInput({ placeholder: '用户名' })
        .onChange((value) => {
          this.formData.username = value
        })
      
      TextInput({ placeholder: '密码' })
        .type(InputType.Password)
        .onChange((value) => {
          this.formData.password = value
        })
    }
  }
}
  1. 避免过度使用
@Component
struct OptimizedComponent {
  // ✅ 只对需要双向绑定的数据使用@Link
  @Link editableData: string
  
  // ✅ 只读数据使用@Prop
  @Prop readOnlyData: string
  
  // ✅ 组件内部状态使用@State
  @State localState: number = 0

  build() {
    Column() {
      // 实现UI
    }
  }
}

4.4 @Provide/@Consume修饰符

4.4.1 基本概念

@Provide和@Consume用于实现跨组件层级的数据共享,避免多层props传递(即"prop drilling"问题)。

4.4.2 基础用法


interface IUser{
  name:string,
  role:string
}
// 顶层组件
@Entry
@Component
struct TestCase {
  @Provide('theme') theme: string = 'light'
  @Provide('user') userInfo:IUser  = {
    name: 'tata',
    role: 'admin'
  }

  build() {
    Column() {
      ConsumerComponent()
    }
  }
}

// 消费组件(可以是任意层级的子组件)
@Component
struct ConsumerComponent {
  @Consume('theme') theme: string
  @Consume('user') userInfo: IUser

  build() {
    Column() {
      Text(`Current Theme: ${this.theme}`)
      Text(`User: ${this.userInfo.name}`)
    }
  }
}

4.4.3 高级特性

  1. 使用别名
@Component
struct AdvancedProvider {
  @Provide('systemTheme') theme: string = 'light'
}

@Component
struct AdvancedConsumer {
  // 使用别名访问共享数据
  @Consume('systemTheme') currentTheme: string

  build() {
    Column() {
      Text(`Theme: ${this.currentTheme}`)
    }
  }
}
  1. 多层级数据共享
@Component
struct AppRoot {
  @Provide('globalState') userInfo:IUser = {
    name:'tata',
    role:'admin'
  }

  build() {
    Column() {
      // 可以被任意深度的子组件访问
      DeepNestedComponent()
    }
  }
}

4.4.4 使用建议与最佳实践

  1. 合理的数据粒度
// ✅ 推荐:适当的粒度
@Component
struct GoodProvider {
  @Provide('userSettings') settings: UserSettings
  @Provide('appTheme') theme: Theme
}

// ❌ 避免:过大的共享范围
@Component
struct BadProvider {
  @Provide('entireAppState') state: AppState
}
  1. 性能优化
@Component
struct OptimizedProvider {
  // 将频繁变化的数据和稳定数据分开
  @Provide('staticConfig') staticConfig: Config // 稳定数据
  @Provide('dynamicState') dynamicState: State // 变化数据

  build() {
    Column() {
      // 实现UI
    }
  }
}
  1. 错误处理
@Component
struct SafeConsumer {
  @Consume('data') data?: DataType  // 使用可选类型
  
  build() {
    Column() {
      if (this.data) {
        // 安全地使用数据
        Text(this.data.toString())
      } else {
        // 提供后备UI
        Text('数据未就绪')
      }
    }
  }
}

4.5 @Watch修饰符

4.5.1 基本概念

@Watch用于监听状态变量的变化,当被监听的状态发生改变时,会触发相应的回调函数。

状态变量改变
Watch监听
触发回调函数
执行自定义逻辑
更新UI/处理数据

4.5.2 基础用法

// 顶层组件
@Entry
@Component
struct TestCase {
  @State @Watch('onCountChange') count: number = 0
  @State message: string = ''

  onCountChange() {
    this.message = `计数变为 ${this.count.toString()}`
  }

  build() {
    Column({ space: 20 }) {
      Text(`Current count: ${this.count}`)
      Text(this.message)

      Button('Increment')
        .onClick(() => {
          this.count++
        })
    }
  }
}

4.5.3 高级用法

  1. 监听多个属性
@Component
struct MultiWatchDemo {

  @State @Watch('onScoreChange') score: number = 0
  @State @Watch('onScoreChange') bonus: number = 0

  // 监听多个数值变化
  onScoreChange() {
    console.info(`Total: ${this.score + this.bonus}`)
  }

  build() {
    Column() {
      // UI实现
    }
  }
}
  1. 条件监听
@Component
struct ConditionalWatchDemo {
  @State @Watch('onValueChange') value: number = 0
  @State isEnabled: boolean = true

  onValueChange(newValue: number, oldValue: number) {
    if (this.isEnabled) {
      // 只在启用状态下处理变化
      this.handleValueChange(newValue, oldValue)
    }
  }

  private handleValueChange(newVal: number, oldVal: number) {
    // 处理逻辑
  }

  build() {
    Column() {
      Switch()
        .checked(this.isEnabled)
        .onChange((value: boolean) => {
          this.isEnabled = value
        })
    }
  }
}

4.5.4 @Watch使用场景

  1. 表单验证
@Component
struct FormValidationDemo {
  @State @Watch('validateEmail') email: string = ''
  @State emailError: string = ''

  validateEmail(newValue: string) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    if (!emailRegex.test(newValue)) {
      this.emailError = '请输入有效的邮箱地址'
    } else {
      this.emailError = ''
    }
  }

  build() {
    Column({ space: 10 }) {
      TextInput({ placeholder: '请输入邮箱' })
        .onChange((value: string) => {
          this.email = value
        })
      
      if (this.emailError) {
        Text(this.emailError)
          .fontSize(12)
          .fontColor(Color.Red)
      }
    }
  }
}
  1. 数据同步
@Component
struct DataSyncDemo {
  @State @Watch('syncToServer') localData: IUser = {}

  async syncToServer(newValue: IUser) {
    try {
      await this.saveToServer(newValue)
      console.info('Data synced successfully')
    } catch (error) {
      console.error('Sync failed:', error)
    }
  }

  private async saveToServer(data: object) {
    // 实现服务器同步逻辑
  }

  build() {
    Column() {
      // UI实现
    }
  }
}

4.5.5 最佳实践

  1. 性能优化

@Component
struct WatchOptimizationDemo {
  @State @Watch('onDataChange') data: IUser = {
    name:'tata',
    role:'admin'
  }

  // ✅ 优化:添加防抖处理
  private debounceTimer: number = -1

  onDataChange() {
    if (this.debounceTimer !== -1) {
      clearTimeout(this.debounceTimer)
    }

    this.debounceTimer = setTimeout(() => {
      this.processDataChange(this.data)
      this.debounceTimer = -1
    }, 300)
  }

  private processDataChange(data: IUser) {
    // 处理数据变化
  }

  build() {
    Column() {
      // UI实现
    }
  }
}

  1. 错误处理
@Component
struct WatchErrorHandlingDemo {
  @State @Watch('onDataChange') data: IUser = {
    name:'tata',
    role:'admin'
  }

  onDataChange() {
    try {
      // 数据验证
      if (!this.isValidData(this.data)) {
        throw new Error('Invalid data format')
      }

      // 处理数据
      this.processData(this.data)
    } catch (error) {
      console.error('Error in watch handler:', error)
      // 恢复到之前的值
      // 显示错误提示
      this.showError(error.message)
    }
  }

  private isValidData(data: IUser): boolean {
    // 实现数据验证逻辑
    return true
  }

  private processData(data: IUser) {
    // 处理数据
  }

  private showError(message: string) {
    // 显示错误提示
  }

  build() {
    Column() {
      // UI实现
    }
  }
}

4.5.6 @Watch注意事项

  1. 避免无限循环
@Component
struct WatchCycleDemo {
  @State @Watch('onValueChange') value1: number = 0
  @State @Watch('onValue2Change') value2: number = 0

  // ❌ 错误:可能导致无限循环
  onValueChange() {
    this.value2++ // 触发value2的watch
  }

  onValue2Change() {
    this.value1++ // 触发value1的watch
  }

  // ✅ 正确:添加循环保护
  private updateCount: number = 0
  private readonly MAX_UPDATES: number = 3

  onSafeValueChange() {
    if (this.updateCount < this.MAX_UPDATES) {
      this.updateCount++
      this.value2++
    } else {
      this.updateCount = 0
    }
  }
}
  1. 同步与异步处理
@Component
struct WatchAsyncDemo {
  @State @Watch('onDataChange') data: object = {}

  // ✅ 处理异步操作
  async onDataChange(newValue: object) {
    try {
      await this.validateData(newValue)
      await this.saveData(newValue)
    } catch (error) {
      console.error('Async operation failed:', error)
    }
  }

  private async validateData(data: object): Promise<void> {
    // 异步数据验证
  }

  private async saveData(data: object): Promise<void> {
    // 异步保存数据
  }

  build() {
    Column() {
      // UI实现
    }
  }
}
  • @Watch 回调函数实际上不支持有效的参数传递
  • 回调函数中第一个参数是被监听属性的名称字符串

4.6 @Observed/@ObjectLink修饰符

4.6.1 基本概念

@Observed和@ObjectLink是用于对象类型数据的响应式管理修饰符:

  • @Observed:将一个类标记为可观察对象
  • @ObjectLink:用于在组件间建立对象的引用关系,实现对象级别的双向同步
Observed类
创建可观察对象
ObjectLink引用
子组件
响应式更新
父组件

4.6.2 基础用法

// 定义可观察类
@Observed
class Student {
  name: string
  age: number
  scores: number[]

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
    this.scores = []
  }
}

// 父组件
@Component
struct ParentComponent {
  // 创建可观察对象实例
  @State student: Student = new Student('tata', 18)

  build() {
    Column() {
      // 传递给子组件
      StudentCard({ studentInfo: this.student })
      
      Button('修改信息')
        .onClick(() => {
          this.student.age++
          this.student.scores.push(100)
        })
    }
  }
}

// 子组件
@Component
struct StudentCard {
  // 使用ObjectLink引用父组件的对象
  @ObjectLink studentInfo: Student

  build() {
    Column() {
      Text(`姓名: ${this.studentInfo.name}`)
      Text(`年龄: ${this.studentInfo.age}`)
      ForEach(this.studentInfo.scores, (score:number) => {
        Text(`分数: ${score}`)
      })
    }
  }
}

4.6.3 关键点

  1. 类级别装饰器
// ✅ 正确:整个类使用 @Observed
@Observed
class Model {
  property1: string
  property2: number
}

// ❌ 错误:不能装饰属性
class WrongModel {
  @Observed property: string  // 这是错误的用法
}
  1. 嵌套对象处理
@Observed
class Parent {
  child: Child  // 如果 Child 需要观察,Child 类也需要使用 @Observed 装饰器
}

@Observed
class Child {
  name: string
}
  1. 数组处理
@Observed
class TodoList {
  items: Array<TodoItem>  // TodoItem 类需要使用 @Observed

  addItem(item: TodoItem) {
    this.items.push(item)
  }
}

@Observed
class TodoItem {
  content: string
  completed: boolean
}

4.6.4 实际应用示例

// 定义观察类
@Observed
class Profile {
  name: string
  age: number

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

// 父组件
@Component
struct ProfileManager {
  @State profile: Profile = new Profile('tata', 25)

  build() {
    Column() {
      // 传递给子组件
      ProfileEditor({ userProfile: this.profile })
      ProfileDisplay({ userProfile: this.profile })
    }
  }
}

// 编辑组件
@Component
struct ProfileEditor {
  @ObjectLink userProfile: Profile

  build() {
    Column() {
      Button('修改年龄')
        .onClick(() => {
          this.userProfile.age++  // 直接修改将触发更新
        })
    }
  }
}

// 显示组件
@Component
struct ProfileDisplay {
  @ObjectLink userProfile: Profile

  build() {
    Column() {
      Text(`姓名: ${this.userProfile.name}`)
      Text(`年龄: ${this.userProfile.age}`)
    }
  }
}

4.6.5 注意事项

  1. @Observed 只能用于类声明
  2. 不能用于装饰类的属性
  3. 嵌套对象如需响应式,需要分别使用 @Observed 装饰器
  4. 配合 @ObjectLink 使用来实现组件间的数据同步
  5. 观察对象的属性修改会自动触发相关组件的更新

对比

让我详细对比这些修饰符的区别:

状态管理修饰符
State
Prop
Link
Observed/ObjectLink
组件内状态管理
单向数据流
双向数据绑定
对象级响应式

1. 使用场景对比

修饰符使用对象数据流向使用场景
@State基本类型/对象/数组组件内部管理组件内部状态
@Prop基本类型/对象/数组父 -> 子父组件向子组件传递数据
@Link基本类型/对象/数组双向父子组件双向数据同步
@Observed/@ObjectLink类实例多组件共享复杂对象的响应式管理

2. 具体示例对比

// @State:组件内部状态管理
@Component
struct StateExample {
  @State count: number = 0  // 组件内部状态

  build() {
    Column() {
      Text(`Count: ${this.count}`)
      Button('Add').onClick(() => this.count++)
    }
  }
}

// @Prop:单向数据流
@Component
struct PropExample {
  @Prop title: string  // 从父组件接收数据
  
  build() {
    Text(this.title)
  }
}

// @Link:双向数据绑定
@Component
struct LinkExample {
  @Link message: string  // 与父组件双向绑定
  
  build() {
    TextInput({ text: this.message })
      .onChange((value: string) => {
        this.message = value  // 修改会影响父组件
      })
  }
}

// @Observed:对象响应式
@Observed
class User {
  name: string
  age: number

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

@Component
struct ObservedExample {
  @State user: User = new User('tata', 25)

  build() {
    Column() {
      UserCard({ userInfo: this.user })  // 传递给子组件
    }
  }
}

@Component
struct UserCard {
  @ObjectLink userInfo: User  // 引用观察对象

  build() {
    Column() {
      Text(`Name: ${this.userInfo.name}`)
      Button('Age++')
        .onClick(() => {
          this.userInfo.age++  // 直接修改对象属性
        })
    }
  }
}

3. 特点对比

@State
  • 用于组件内部状态管理
  • 值变化会触发组件重新渲染
  • 可以管理任何类型的数据
@State count: number = 0
@State user: Object = {}
@State list: string[] = []
@Prop
  • 实现单向数据流
  • 子组件不能直接修改父组件数据
  • 父组件数据变化会更新子组件
// 父组件
@State title: string = 'Hello'
// 子组件使用
@Prop title: string
@Link
  • 实现双向数据绑定
  • 子组件可以直接修改数据
  • 数据变化双向同步
// 父组件
@State message: string = ''
// 子组件使用
@Link message: string
@Observed/@ObjectLink
  • 用于复杂对象的响应式管理
  • 自动跟踪对象属性变化
  • 支持多组件共享和同步
@Observed
class Model {
  // 整个类的属性都会被观察
}

@ObjectLink model: Model  // 引用观察对象

4. 数据同步方式对比

// @State:组件内直接修改
@State value: number = 0
this.value = 1  // 直接赋值

// @Prop:不能直接修改
@Prop title: string
this.title = 'new'  // ❌ 错误

// @Link:双向同步
@Link count: number
this.count++  // ✅ 会同步到父组件

// @Observed:对象属性修改
@ObjectLink user: User
this.user.name = 'new name'  // ✅ 自动触发更新

在这里插入图片描述

;