一、概述
在鸿蒙Next中,@Observed
和@ObjectLink
装饰器用于处理嵌套类对象属性变化,实现双向数据同步,弥补了其他装饰器只能观察一层变化的局限。从API version 9开始,这两个装饰器支持在ArkTS卡片中使用,从API version 11开始,支持在元服务中使用。
(一)功能特性
@Observed
用于标记类,使其创建的实例能被观察到属性变化,主要用于嵌套类场景。@ObjectLink
装饰子组件中的状态变量,接收@Observed
装饰的类的实例,与父组件中对应的状态变量建立双向数据绑定。
二、装饰器说明
(一)@Observed
类装饰器
- 参数:无。
- 类装饰器:用于修饰class,需放在class定义前,通过
new
创建类对象。
(二)@ObjectLink
变量装饰器
- 参数:无。
允许装饰的变量类型
- 必须为被
@Observed
装饰的class实例,且必须指定类型。 - 不支持简单类型,若需使用简单类型可考虑
@Prop
。 - 支持继承Date、Array的class实例,API11及以上支持继承Map、Set的class实例,API11及以上还支持
@Observed
装饰类和undefined或null组成的联合类型。 - 变量属性可改变,但变量自身分配不允许,即该装饰器装饰的变量只读,不能被重新赋值(整体替换除外,但要在父组件进行)。
- 必须为被
三、变量的传递/访问规则
从父组件初始化
- 必须指定。
初始化
@ObjectLink
装饰的变量需同时满足以下条件:- 类型必须是
@Observed
装饰的class。 - 初始化数值需是数组项或class的属性。
- 同步源的class或数组必须是
@State
、@Link
、@Provide
、@Consume
或者@ObjectLink
装饰的数据。
- 类型必须是
- 与源对象同步:双向同步。
- 可以初始化子组件:允许,可用于初始化常规变量、
@State
、@Link
、@Prop
、@Provide
。
四、观察变化和行为表现
(一)观察变化
@Observed
装饰的类- 若属性为非简单类型(如class、Object或数组),也需被
@Observed
装饰,否则其属性变化无法观察到。
- 若属性为非简单类型(如class、Object或数组),也需被
@ObjectLink
装饰的变量- 可观察到属性数值的变化(属性指Object.keys返回的所有属性)。
- 若数据源是数组,可观察到数组item的替换;若数据源是class,可观察到class的属性变化。
- 对于继承Date、Map、Set的class实例,可分别观察到相应类型的整体赋值及通过接口更新值的操作(如Date的日期属性更新、Map的键值更新、Set的元素更新等)。
(二)框架行为
初始渲染
@Observed
装饰的class的实例被不透明代理对象包装,代理其属性的setter和getter方法。- 子组件中
@ObjectLink
装饰的变量从父组件初始化,接收@Observed
装饰的class的实例,@ObjectLink
的包装类将自己注册给@Observed class
。
属性更新
- 当
@Observed
装饰的class属性改变时,执行代理的setter和getter,遍历依赖它的@ObjectLink
包装类,通知数据更新。
- 当
五、使用场景示例
(一)嵌套对象
- 定义了
Bag
、User
、Book
、BookName
等类,均被@Observed
装饰,形成嵌套对象结构。 - 在
ViewA
、ViewC
子组件中通过@ObjectLink
接收父组件ViewB
中@State
变量的属性(如Bag
实例或BookName
实例),实现双向同步。 - 在
ViewB
中对user.bag
、child.bookName.size
等属性的修改,能在相关子组件中同步更新。注意@ObjectLink
变量只读,不能进行整体赋值操作(如this.bookName = new bookName(...)
),但可更改成员属性(如this.bookName.size += 1
)。
(二)对象数组
- 定义
Info
类被@Observed
装饰,Parent
组件中有@State
装饰的Info[]
数组。 Child
子组件通过@ObjectLink
接收Info
实例,实现双向同步。- 在
Parent
中对数组的操作(如push
、shift
、修改数组项属性等)会触发不同的更新效果,涉及ForEach
的重新执行和相关子组件的刷新,而对于@State
无法观察到的第二层属性变化(如Info
类中的info
属性),由于Info
被@Observed
装饰,可被@ObjectLink
观察到并同步更新。
(三)二维数组
- 声明
StringArray
类继承Array<string>
并被@Observed
装饰,通过new
创建实例。 - 在
IndexPage
组件中定义@State
装饰的Array<StringArray>
二维数组,ItemPage
子组件通过@ObjectLink
接收StringArray
实例,实现双向同步。 - 在
IndexPage
中对二维数组元素的操作(如push
)会触发相应的UI更新。
(四)继承Map类(API11及以上)
- 定义
Info
类包含MyMap<number, string>
类型的属性,MyMap
类需被@Observed
装饰(此处假设已定义)。 - 在相关组件中,通过
@ObjectLink
实现对MyMap
类型数据的双向同步,点击按钮改变MyMap
属性时,视图会随之刷新。同理,对于继承Set类也类似(可参考继承Set类部分的示例)。
六、限制条件
- 使用
@Observed
装饰class会改变其原始原型链,与其他类装饰器一起使用可能导致问题。 @ObjectLink
装饰器不能在@Entry
装饰的自定义组件中使用。@ObjectLink
装饰的变量类型需为显式的被@Observed
装饰的类,未指定类型或类型错误在编译期会报错。@ObjectLink
装饰的变量不能本地初始化,只能通过构造参数从父组件传入初始值,否则编译报错。且变量只读,赋值操作会导致运行时报错(除在父组件进行整体替换)。
七、常见问题及注意事项
(一)在子组件中给@ObjectLink
装饰的变量赋值
不允许直接赋值,会导致同步链打断和运行时报错,应在父组件进行整体替换或仅修改其属性。
(二)基础嵌套对象属性更改失效
确保嵌套对象中的类及其属性(非简单类型)都被@Observed
装饰,否则属性变化无法被观察到。
(三)复杂嵌套对象属性更改失效
如二维数组等复杂嵌套结构,同样要保证各层级相关类都被@Observed
装饰,以实现属性变化的观察和同步。
(四)@Prop
与@ObjectLink
的差异
@Prop
装饰的变量与数据源单向同步,允许本地更改但父组件数据源更新时本地修改会被覆盖;@ObjectLink
装饰的变量与数据源双向同步,相当于指向数据源的指针,只读且不能重新赋值(整体替换在父组件进行)。
(五)在@Observed
装饰类的构造函数中延时更改成员变量
不建议在构造函数中进行延时更改成员变量的操作,可能导致预期外的行为,应在合适的时机修改属性以确保变化可被观察和同步。
(六)@ObjectLink
数据源更新时机
数据源(@Observed
装饰的类实例)的属性变化会触发@ObjectLink
装饰变量的更新,从而更新相关UI组件,但要注意同步机制和触发条件,如ForEach
的更新机制对数组操作的影响等。
(七)使用a.b(this.object)
形式调用,不会触发UI刷新
当@ObjectLink
装饰的变量是Object类型,且在build
方法内通过a.b(this.object)
形式调用(如静态方法或组件内部方法修改Object属性)时,无法触发UI刷新。解决方法是先对变量进行赋值,使修改操作作用于带有Proxy代理的变量,从而实现UI刷新。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。