文章目录
- 一、引言
- 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
等等。
1.1 什么是修饰符
修饰符是一种特殊的语法元素,用@符号作为标识,可以用来修饰类、方法、属性等代码元素,为其添加额外的行为或特性。在鸿蒙开发中,修饰符主要用于以下几个方面:
- UI样式定义和复用
- 组件声明和管理
- 状态管理和数据同步
- 组件间通信
1.2 修饰符在鸿蒙开发中的重要性
1.3 修饰符的作用机制
修饰符在编译时会被转换为特定的代码,它们主要通过以下机制发挥作用:
- 装饰器模式: 在不改变原有代码结构的情况下,动态地给对象添加新的功能
- 编译时转换: 将声明式的代码转换为命令式代码
- 运行时注入: 在运行时为目标对象注入额外的行为
二、UI装饰类修饰符
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 使用建议
-
选择合适的场景
- 需要创建特定样式的组件变体时,使用@Extend
- 需要跨组件复用样式时,使用@Styles
-
命名规范
// 好的命名示例
@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()方法
- 支持组件生命周期
- 可以包含状态变量
- 支持组件间通信
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 最佳实践
- 组件拆分原则
// 好的实践:功能单一,职责明确
@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("其他信息")
}
}
}
- 生命周期使用建议
@Component
struct LifecycleComponent {
@State private data: any = {}
aboutToAppear() {
// 初始化数据,订阅事件
this.initData()
}
aboutToDisappear() {
// 清理资源,取消订阅
this.cleanUp()
}
private initData() {
// 数据初始化逻辑
}
private cleanUp() {
// 清理逻辑
}
build() {
// UI渲染
}
}
3.1.6 性能优化建议
- 状态管理优化
@Component
struct OptimizedComponent {
// 使用私有变量存储不需要响应式更新的数据
private staticData: string = 'static'
// 只将需要触发更新的数据声明为@State
@State dynamicData: number = 0
build() {
Column() {
// 静态内容
Text(this.staticData)
// 动态内容
Text(`${this.dynamicData}`)
}
}
}
- 条件渲染优化
@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 使用场景
- 自定义列表项渲染
@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)
}
}
})
}}
}
- 可定制化容器组件
@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 注意事项与最佳实践
- 参数命名规范
// 推荐的命名方式
@BuilderParam headerBuilder: () => void
@BuilderParam contentBuilder: () => void
@BuilderParam footerBuilder: () => void
// 不推荐的命名方式
@BuilderParam hdr: () => void
@BuilderParam cnt: () => void
- 性能考虑
@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。
4.1.2 使用规则
- 基本语法
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注意事项和最佳实践
- 状态初始化
@Component
struct StateInitDemo {
// ✅ 推荐:直接初始化
@State counter1: number = 0
// ⚠️ 不推荐:延迟初始化可能导致问题
@State counter2: number
aboutToAppear() {
// 延迟初始化可能导致首次渲染问题
this.counter2 = 0
}
}
- 状态粒度控制
@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的高级用法
- 默认值处理
@Component
struct PropDefaultDemo {
// 使用装饰器提供默认值
@Prop defaultValue: number = 100
build() {
Column() {
Text(`Value: ${this.defaultValue}`)
}
}
}
- 属性监听与处理
@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 最佳实践
- 属性命名规范
@Component
struct PropNamingDemo {
// ✅ 推荐:语义化命名
@Prop isVisible: boolean
@Prop userName: string
@Prop itemCount: number
// ❌ 避免:模糊的命名
@Prop flag: boolean
@Prop str: string
@Prop num: number
}
- 类型安全
@Component
struct PropTypeDemo {
// ✅ 推荐:明确的类型定义
@Prop items: Array<{
id: number,
name: string
}>
// ❌ 避免:使用any类型
@Prop data: any
}
4.3 @Link修饰符
4.3.1 基本概念
@Link修饰符用于实现父子组件间的双向数据绑定。与@Prop不同,@Link装饰的变量在子组件中的修改会同步回父组件。
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最佳实践
- 合理使用场景
@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
})
}
}
}
- 避免过度使用
@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 高级特性
- 使用别名
@Component
struct AdvancedProvider {
@Provide('systemTheme') theme: string = 'light'
}
@Component
struct AdvancedConsumer {
// 使用别名访问共享数据
@Consume('systemTheme') currentTheme: string
build() {
Column() {
Text(`Theme: ${this.currentTheme}`)
}
}
}
- 多层级数据共享
@Component
struct AppRoot {
@Provide('globalState') userInfo:IUser = {
name:'tata',
role:'admin'
}
build() {
Column() {
// 可以被任意深度的子组件访问
DeepNestedComponent()
}
}
}
4.4.4 使用建议与最佳实践
- 合理的数据粒度
// ✅ 推荐:适当的粒度
@Component
struct GoodProvider {
@Provide('userSettings') settings: UserSettings
@Provide('appTheme') theme: Theme
}
// ❌ 避免:过大的共享范围
@Component
struct BadProvider {
@Provide('entireAppState') state: AppState
}
- 性能优化
@Component
struct OptimizedProvider {
// 将频繁变化的数据和稳定数据分开
@Provide('staticConfig') staticConfig: Config // 稳定数据
@Provide('dynamicState') dynamicState: State // 变化数据
build() {
Column() {
// 实现UI
}
}
}
- 错误处理
@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用于监听状态变量的变化,当被监听的状态发生改变时,会触发相应的回调函数。
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 高级用法
- 监听多个属性
@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实现
}
}
}
- 条件监听
@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使用场景
- 表单验证
@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)
}
}
}
}
- 数据同步
@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 最佳实践
- 性能优化
@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实现
}
}
}
- 错误处理
@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注意事项
- 避免无限循环
@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
}
}
}
- 同步与异步处理
@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:用于在组件间建立对象的引用关系,实现对象级别的双向同步
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 关键点
- 类级别装饰器
// ✅ 正确:整个类使用 @Observed
@Observed
class Model {
property1: string
property2: number
}
// ❌ 错误:不能装饰属性
class WrongModel {
@Observed property: string // 这是错误的用法
}
- 嵌套对象处理
@Observed
class Parent {
child: Child // 如果 Child 需要观察,Child 类也需要使用 @Observed 装饰器
}
@Observed
class Child {
name: string
}
- 数组处理
@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 注意事项
- @Observed 只能用于类声明
- 不能用于装饰类的属性
- 嵌套对象如需响应式,需要分别使用 @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' // ✅ 自动触发更新