鸿蒙修饰符

文章目录

  • 一、引言
    • 1.1 什么是修饰符
    • 1.2 修饰符在鸿蒙开发中的重要性
    • 1.3 修饰符的作用机制
  • 二、UI装饰类修饰符
    • 2.1 @Styles修饰符
      • 2.1.1 基本概念和使用场景
      • 2.1.2 使用示例
      • 2.1.3 最佳实践
    • 2.2 @Extend修饰符
      • 2.2.1 基本概念
      • 2.2.2 使用示例
      • 2.2.3 @Extend vs @Styles 对比
      • 2.2.4 使用建议
  • 三、组件类修饰符
    • 3.1 @Component修饰符
      • 3.1.1 基本概念
      • 3.1.2 @Component vs 普通函数对比
      • 3.1.3 基础使用示例
      • 3.1.4 高级特性
      • 3.1.5 最佳实践
      • 3.1.6 性能优化建议
    • 3.2 @BuilderParam修饰符
      • 3.2.1 基本概念
      • 3.2.2 使用场景
      • 3.2.3 注意事项与最佳实践
  • 四、状态管理类修饰符
    • 4.1 @State修饰符
      • 4.1.1 基本概念
      • 4.1.2 使用规则
      • 4.1.3 @State注意事项和最佳实践
    • 4.2 @Prop修饰符
      • 4.2.1 基本概念
      • 4.2.2 使用示例
      • 4.2.3 @Prop vs 普通变量对比
      • 4.2.4 @Prop的高级用法
      • 4.2.5 最佳实践
    • 4.3 @Link修饰符
      • 4.3.1 基本概念
      • 4.3.2 使用示例
      • 4.3.3 @Link vs @Prop 对比
      • 4.3.4 @Link最佳实践
    • 4.4 @Provide/@Consume修饰符
      • 4.4.1 基本概念
      • 4.4.2 基础用法
      • 4.4.3 高级特性
      • 4.4.4 使用建议与最佳实践
    • 4.5 @Watch修饰符
      • 4.5.1 基本概念
      • 4.5.2 基础用法
      • 4.5.3 高级用法
      • 4.5.4 @Watch使用场景
      • 4.5.5 最佳实践
      • 4.5.6 @Watch注意事项
    • 4.6 @Observed/@ObjectLink修饰符
      • 4.6.1 基本概念
      • 4.6.2 基础用法
      • 4.6.3 关键点
      • 4.6.4 实际应用示例
      • 4.6.5 注意事项
    • 对比
      • 1. 使用场景对比
      • 2. 具体示例对比
      • 3. 特点对比
        • @State
        • @Prop
        • @Link
        • @Observed/@ObjectLink
      • 4. 数据同步方式对比

一、引言

在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'  // ✅ 自动触发更新

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/926712.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

输入json 达到预览效果

下载 npm i vue-json-pretty2.4.0 <template><div class"newBranchesDialog"><t-base-dialogv-if"addDialogShow"title"Json数据配置"closeDialog"closeDialog":dialogVisible"addDialogShow":center"…

MDETR - Modulated Detection for End-to-End Multi-Modal Understanding

Abstract 多模态推理系统&#xff08;如视觉问答系统或图像与文本之间的匹配系统&#xff09;通常依赖于一个预训练的物体检测器&#xff0c;用于从图像中提取感兴趣的区域。这些物体检测器通常是作为“黑盒”使用的&#xff0c;也就是说&#xff0c;它们在被训练时独立于下游…

pytest+allure生成报告显示loading和404

pytestallure执行测试脚本后&#xff0c;通常会在电脑的磁盘上建立一个临时文件夹&#xff0c;里面存放allure测试报告&#xff0c;但是这个测试报告index.html文件单独去打开&#xff0c;却显示loading和404, 这个时候就要用一些办法来解决这个报告显示的问题了。 用命令产生…

【MyBatis】验证多级缓存及 Cache Aside 模式的应用

文章目录 前言1. 多级缓存的概念1.1 CPU 多级缓存1.2 MyBatis 多级缓存 2. MyBatis 本地缓存3. MyBatis 全局缓存3.1 MyBatis 全局缓存过期算法3.2 CacheAside 模式 后记MyBatis 提供了缓存切口&#xff0c; 采用 Redis 会引入什么问题&#xff1f;万一遇到需强一致场景&#x…

MySQL —— MySQL 程序

目录 前言 一、MySQL 程序简介 二、mysqld -- MySQL 服务器 三、mysql -- MySQL 客户端 1. mysql 客户端简介 2. mysql 客户端选项 &#xff08;1&#xff09;指定选项的方式 &#xff08;2&#xff09;mysql 客户端命令常用选项 &#xff08;3&#xff09;在命令行中使…

统一功能(2异常)

RequestMapping("/r1")public String returnString(){return "asdfghjkl";} 如果在RequestMapping中返回的类型是String但是统一返回结果是Result类就会报错。 原因&#xff1a;源码中需要Result类型但是传入的是String类型 统一异常处理&#xff1a; 比如…

浅谈js中onmouseleave和onmouseout的区别

同步发布于我的网站 &#x1f680; 背景介绍基本概念区别详解 无子元素的情况有子元素的情况 实际应用场景 使用 onmouseleave使用 onmouseout 注意事项总结 背景介绍 在前端开发中&#xff0c;我们经常需要为元素绑定鼠标事件&#xff0c;以实现各种交互效果。onmouseleave…

python+django自动化部署日志采用‌WebSocket前端实时展示

一、开发环境搭建和配置 # channels是一个用于在Django中实现WebSocket、HTTP/2和其他异步协议的库。 pip install channels#channels-redis是一个用于在Django Channels中使用Redis作为后台存储的库。它可以用于处理#WebSocket连接的持久化和消息传递。 pip install channels…

【小白学机器学习37】用numpy计算协方差cov(x,y) 和 皮尔逊相关系数 r(x,y)

目录 1 关于1个数组np.array&#xff08;1组数据&#xff09;如何求各种统计数据 2 关于2个数组np.array&#xff08;2组数据&#xff09;如何求数组的相关关系&#xff1f; 2.1 协方差公式和方差公式 2.2 协方差 公式 的相关说明 2.3 用np.cov(x,y,ddof0) 直接求协方差矩…

(超详细图文)PLSQL Developer 配置连接远程 Oracle 服务

1、下载配置文件 &#xff08;超详细图文详情&#xff09;Navicat 配置连接 Oracle-CSDN博客 将下载的文件解压到单独文件夹&#xff0c;如&#xff1a;D:\App\App_Java\Oracle\instantclient-basic-windows.x64-19.25.0.0.0dbru 2、配置 打开 PLSQL Developer&#xff0c;登…

学习视频超分辨率扩散模型中的空间适应和时间相干性(原文翻译)

文章目录 摘要1. Introduction2. Related Work3. Our Approach3.1. Video Upscaler3.2. Spatial Feature Adaptation Module3.3. Temporal Feature Alignment Module3.4. Video Refiner3.5. Training Strategy 4. Experiments4.1. Experimental Settings4.2. Comparisons with …

设计模式---建造者模式

建造者模式 一种创建型设计模式&#xff0c;它允许你一步一步地构建复杂对象。通过使用建造者模式&#xff0c;你可以将对象的构建过程与其表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。说白点就是&#xff0c;解决了构造函数创建对象的问题。 适用于那种构造函…

AtomicIntegerFieldUpdater能否降低内存

1. 代码如下&#xff1a; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {final AtomicInteger startPosition new AtomicInteger(0);final AtomicInteger wrotePosition new Atom…

element的el-table表格标题用css自定义是否必填,用添加伪类的方式标红色*

element的el-table表格标题用css自定义是否必填添加伪类红色 * 效果图如下&#x1f447; el-table组件的html部分 css部分 /deep/.el-table__header-wrapper{.el-table__header{.has-gutter tr .el-table__cell:nth-of-type(3) .cell:before{content: *;color:red}.has-gutte…

数据库的⽤户和权限管理

数据库的⽤户和权限管理 应⽤场景⽤户查看⽤户创建⽤户语法注意事项示例 修改密码语法示例 删除⽤户语法 权限与授权给⽤户授权语法⽰例 回收权限语法⽰例 应⽤场景 数据库服务安装成功后默认有⼀个root⽤户&#xff0c;可以新建和操纵数据库服务中管理的所有数据库。在真实的…

C语言数据结构-栈和队列

C语言数据结构-栈和队列 1.栈1.1概念与结构1.2栈的实现1.2.1结构体定义1.2.2初始化1.2.3销毁1.2.4入栈1.2.5出栈1.2.6取栈顶处的元素1.2.7获取栈中有效的个数 2.队列2.1概念与结构2.2队列的实现2.2.1结构体定义2.2.2入队列2.2.3判断是否为空2.2.4队列中的有效元素个数2.2.5删除…

[CTF/网络安全] 攻防世界 upload1 解题详析

[CTF/网络安全] 攻防世界 upload1 解题详析 考察文件上传&#xff0c;具体原理及姿势不再赘述。 姿势 在txt中写入一句话木马<?php eval($_POST[qiu]);?> 回显如下&#xff1a; 查看源代码&#xff1a; Array.prototype.contains function (obj) { var i this.…

TiDB 优化器丨执行计划和 SQL 算子解读最佳实践

作者&#xff1a; TiDB社区小助手 原文来源&#xff1a; https://tidb.net/blog/5edb7933 导读 在数据库系统中&#xff0c;查询优化器是数据库管理系统的核心组成部分&#xff0c;负责将用户的 SQL 查询转化为高效的执行计划&#xff0c;因而会直接影响用户体感的性能与稳…

监控视频汇聚平台:Liveweb视频监控管理平台方案详细介绍

Liveweb国标视频综合管理平台是一款以视频为核心的智慧物联应用平台。它基于分布式、负载均衡等流媒体技术进行开发&#xff0c;提供广泛兼容、安全可靠、开放共享的视频综合服务。该平台具备多种功能&#xff0c;包括视频直播、录像、回放、检索、云存储、告警上报、语音对讲、…

10个Word自动化办公脚本

在日常工作和学习中&#xff0c;我们常常需要处理Word文档&#xff08;.docx&#xff09;。 Python提供了强大的库&#xff0c;如python-docx&#xff0c;使我们能够轻松地进行文档创建、编辑和格式化等操作。本文将分享10个使用Python编写的Word自动化脚本&#xff0c;帮助新…