HarmonyOS鸿蒙Next中状态变量V1里的@ObjectLink有什么用吗
HarmonyOS鸿蒙Next中状态变量V1里的@ObjectLink有什么用吗 状态变量V1里的@ObjectLink有什么用吗,实测不使用@Observed/@ObjectLink,只使用@Observed/@state子组件更改对象属性值也可以引起ui渲染:
父元素将对象的内存地址传给了子元素,是是深拷贝,所以子元素里改对象的某个属性父元素也会改变,所以我有疑问:@ObjectLink有啥用??有哪位大佬能帮我写一个单用@Observed实现不了,必须结合@ObjectLink的例子
// 1. 类加 [@Observed](/user/Observed)
[@Observed](/user/Observed)
class User {
name: string=''
constructor(name: string) {
this.name = name
}
}
// 父
@Entry
@Component
struct Parent {
// 2. 必须用 @State 装 Observed 对象
@State user: User = new User('tom')
build() {
Column(){
Text('父'+this.user.name)
Button().onClick(()=>{
this.user.name='A'
})
// 传给子
Child({ u: this.user })
}
}
}
// 子
@Component
struct Child {
@State u: User=new User('jeery')
build() {
Column(){
Text('子'+this.u.name)
Button().onClick(()=>{
this.u.name='B'
})
}
}
}
更多关于HarmonyOS鸿蒙Next中状态变量V1里的@ObjectLink有什么用吗的实战教程也可以访问 https://www.itying.com/category-93-b0.html
有用,而且你这个实测现象不代表 @ObjectLink 没价值,核心是你把两件事混在一起了:
- “对象是不是同一个引用”;
- “父子状态语义是不是双向同步、能不能稳定追踪”。
你这段代码里,子组件的 @State u 确实可能拿到和父组件同一个 User 实例,所以子里改 this.u.name,父里也会变。这不是深拷贝,恰恰更接近“同一个引用对象被两个组件拿着”。
但官方对 @State 的定义很明确:它只是“用父组件传入的值做初始化”,后续不和父组件同步。也就是说,它能跑,不等于它的语义就是父子双向绑定。
所以 @ObjectLink 的作用,不是单纯“让对象引用共享”,而是:
- 明确建立父子双向同步关系;
- 让子组件成为这个被观察对象的正式依赖方;
- 子组件不能擅自把整个对象换掉,只能改属性,避免把同步链打断;
- 处理嵌套对象时,补上 V1 只观察第一层的短板。
示例代码:
[@Observed](/user/Observed)
class User {
name: string
constructor(name: string) {
this.name = name
}
}
@Entry
@Component
struct Parent {
[@State](/user/State) user: User = new User('tom')
build() {
Column() {
Text('父: ' + this.user.name)
Button('父组件修改 name')
.onClick(() => {
this.user.name = 'A'
})
Button('父组件整体替换 user')
.onClick(() => {
this.user = new User('lucy')
})
Child({ u: this.user })
}
}
}
@Component
struct Child {
[@ObjectLink](/user/ObjectLink) u: User
build() {
Column() {
Text('子: ' + this.u.name)
Button('子组件修改 name')
.onClick(() => {
this.u.name = 'B'
})
// 这里不允许 this.u = new User(...)
// [@ObjectLink](/user/ObjectLink) 不能整体赋值,只能改属性
}
}
}
- 子改 this.u.name,父会同步看到;
- 父整体替换 this.user = new User(‘lucy’),子会在父刷新后重新拿到新的数据源;
- 子不能偷偷换整对象,避免同步链断掉。
总结: 你现在的例子能刷新,是因为“共享了同一个被 @Observed 代理过的对象实例”,不是因为 @State 替代了 @ObjectLink。@ObjectLink 真正的价值在于“父子双向同步语义 + 禁止子组件整体换对象 + 嵌套对象单独建观察链”。
更多关于HarmonyOS鸿蒙Next中状态变量V1里的@ObjectLink有什么用吗的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
你这个现象容易让人误解:把对象引用传给子组件后,子组件确实可能改到同一个对象,但这不等于已经建立了可靠的父子状态同步。@ObjectLink 的价值是配合 @Observed,把子组件注册到被观察对象的依赖链里,尤其用于对象数组、嵌套对象、列表项子属性变化这些场景。单层字段有时看起来只靠 @State 也能刷新;但像 user.profile.addr.city 这种深层属性,父组件直接 Text 读取通常不会因为深层字段变化而刷新。实践上建议:父组件 @State 持有外层对象/数组,ForEach 用稳定 id;子组件用 @ObjectLink 接收 @Observed 的数组项或内层对象;如果父组件也要显示深层字段,要么整体替换外层对象,要么把显示深层字段的 UI 也拆到接收 @ObjectLink 的子组件里。
跑了一下demo,整理了一些资料,看看:
在 HarmonyOS ArkUI 里,你的观察是对的:只要类被 @Observed 装饰,且父组件用 [@State](/user/State) 持有对象实例,那么子组件无论用 [@State](/user/State) 还是 [@ObjectLink](/user/ObjectLink) 接收,修改该对象的属性,都能触发子组件的 UI 刷新,并且父组件的对象属性也确实被改变了。
但这不代表 [@ObjectLink](/user/ObjectLink) 没用,它的核心价值在于 “同步引用”,而非仅仅“同步属性变化”。
下面用一个 必须使用 [@ObjectLink](/user/ObjectLink) 否则无法正确渲染 的场景,你马上就能看懂区别。
场景:父组件整体替换对象,子组件需要自动跟随
- 用
[@State](/user/State)接收对象时,子组件存的是初始传入时那份引用的快照。
当父组件把整个user换成另一个新对象时,子组件的[@State](/user/State) u不会更新,UI 停留在旧对象上。 - 用
[@ObjectLink](/user/ObjectLink)接收对象时,子组件与父组件共享同一引用链路。
父组件一替换对象,子组件会立刻同步新引用并刷新 UI。
❌ 错误示例:只用 @State,父组件替换对象后,子组件不更新
@Observed
class User {
name: string = '';
constructor(name: string) {
this.name = name;
}
}
@Entry
@Component
struct Parent {
[@State](/user/State) user: User = new User('tom');
build() {
Column({ space: 10 }) {
Text(`父组件:${this.user.name}`).fontSize(20)
Button('父组件替换整个对象为 Jerry')
.onClick(() => {
// 整体替换成一个新对象
this.user = new User('Jerry');
})
// 子组件用 [@State](/user/State) 接收
ChildByState({ u: this.user })
}
.padding(20)
}
}
@Component
struct ChildByState {
[@State](/user/State) u: User = new User('default'); // 这是本地状态,只初始化一次
build() {
Column() {
Text(`子组件([@State](/user/State)):${this.u.name}`).fontSize(18).fontColor(Color.Red)
}
}
}

运行结果:点击父组件按钮替换对象后,父组件显示 Jerry,但子组件仍然显示 tom。
因为 ChildByState 的 [@State](/user/State) u 只在创建时被赋值一次,后续父组件传入的新引用对它没影响。
✅ 正确示例:使用 @ObjectLink,子组件自动跟随引用变化
@Observed
class User {
name: string = '';
constructor(name: string) {
this.name = name;
}
}
@Entry
@Component
struct Parent {
[@State](/user/State) user: User = new User('tom');
build() {
Column({ space: 10 }) {
Text(`父组件:${this.user.name}`).fontSize(20)
Button('父组件替换整个对象为 Jerry')
.onClick(() => {
// 整体替换
this.user = new User('Jerry');
})
// 子组件用 [@ObjectLink](/user/ObjectLink) 接收
ChildByObjectLink({ u: this.user })
}
.padding(20)
}
}
@Component
struct ChildByObjectLink {
// 关键:[@ObjectLink](/user/ObjectLink) 必须与 @Observed 搭配,且不能设置初始值
[@ObjectLink](/user/ObjectLink) u: User;
build() {
Column() {
Text(`子组件([@ObjectLink](/user/ObjectLink)):${this.u.name}`).fontSize(18).fontColor(Color.Green)
}
}
}

运行结果:点击按钮后,父组件和子组件同时从 tom 变为 Jerry。
因为 [@ObjectLink](/user/ObjectLink) 建立的是一条引用同步通道,而不是一次性拷贝。
总结:@ObjectLink 究竟有什么用?
| 场景 | @State 接收对象 | @ObjectLink 接收对象 |
|---|---|---|
| 修改对象属性 | ✅ 双方都能刷新 | ✅ 双方都能刷新 |
| 父组件替换整个对象 | ❌ 子组件不更新 | ✅ 子组件自动同步新引用 |
| 对象多层嵌套且需要局部刷新 | 可能重建不必要的渲染 | 精确跟踪被修改的路径,性能更好 |
| 避免不必要的深拷贝 | 无深拷贝,但引用断连 | 无拷贝,且引用始终保持一致 |
所以,[@ObjectLink](/user/ObjectLink) 不可或缺的场景就是:父组件会重新赋值整个对象,且要求子组件实时响应。
在你原来的代码里,恰好只测试了“修改属性”,而没测试“替换整个对象”,才会觉得它没用。实际复杂应用中,这种引用级同步非常重要。
官方给的例子很清晰的,正例、反例、各种类型情景,都举了。《使用场景》。
以@State和@ObjectLink修饰对象数组举个例子。
- 两者变化都可影响本组件;修饰数组时,都不能观察到数组项对象属性的变化。
- @ObjectLink还可和父组件和初始化自己关联的状态变量双向影响。
示例:
//被观察者
@Observed
class Fruits{
count:number = 0;
constructor(count:number) {
this.count = count;
}
}
@Component
struct Child{
[@ObjectLink](/user/ObjectLink) fruitArr:Fruits[];//变化可影响本组件和初始化本变量关联的状态变量
[@State](/user/State) fruits:Fruits[] = [new Fruits(0)];//变化只能影响本组件
build() {
Column({space:10}){
Text(`子 State的:${this.fruits[0].count}`)
Text(`子 ObjectLink的:${this.fruitArr[0].count}`)
Button('子State 数组整体赋值')
.onClick((event: ClickEvent) => {
this.fruits = [new Fruits(Math.random()*100)];
})
Button('子State 数组项对象赋值')
.onClick((event: ClickEvent) => {
this.fruits[0] = new Fruits(Math.random()*100);
})
Button('子State 数组项对象属性赋值')
.onClick((event: ClickEvent) => {
this.fruits[0].count = Math.random()*100;
})
Button('子ObjectLink 整体赋值【错误】')
.onClick((event: ClickEvent) => {
//错误,ObjectLink不允许整体赋值,闪退以后不干掉后台重新打开,偶尔状态管理会出现异常
this.fruitArr = [new Fruits(Math.random()*100)];
})
Button('子ObjectLink 数组项对象赋值')
.onClick((event: ClickEvent) => {
this.fruitArr[0] = new Fruits(Math.random()*100);
})
Button('子ObjectLink 数组项对象属性赋值')
.onClick((event: ClickEvent) => {
this.fruitArr[0].count = Math.random()*50;
})
}.width('100%')
.alignItems(HorizontalAlign.Center)
}
}
@Entry
@Component
struct Parent {
[@State](/user/State) fArr:Fruits[] = [new Fruits(9)];
build() {
Column({space:10}){
Text(`父 State的:${this.fArr[0].count}`)
Button('父State 整体赋值')
.onClick((event: ClickEvent) => {
this.fArr = [new Fruits(Math.random()*100)]
})
Button('父State 数组项对象赋值')
.onClick((event: ClickEvent) => {
this.fArr[0] = new Fruits(Math.random()*100)
})
Button('父State 数组项对象属性赋值')
.onClick((event: ClickEvent) => {
this.fArr[0].count = Math.random()*100
})
Divider()
Child({fruitArr:this.fArr, fruits:this.fArr});
}.width('100%')
.alignItems(HorizontalAlign.Center)
}
}
状态管理在响应式设计里是很重要的一环,官方在这一块给了大量详细的解释和示例。可细读。
用你的代码传一个数组对象,看看效果,然后再用下面类,修改一下你的代码,再运行看看效果。
将Array变为ObseredArray,使用Observed监听。
//xxx.ets
@Observed
export class ObservedArray<T> extends Array<T> {
constructor(args?: T[]) {
if (args instanceof Array) {
super(...args);
} else {
super();
}
}
}
感谢您的回复,不过我看这个案例好像没有体现父组件或者子组件两者一项更改,两处组件的ui都发生,
你是
@State cousin: Cousin = new Cousin(10, 20, 30);
但你传给子组件的是child对象而不是cousin
ViewChild({ child: this.cousin.child })
我在父组件加了一行代码,但子组件点击button按钮后ui无法同步更新:
Text(`childId: ${this.cousin.child.childId}`)

@Observed/@ObjectLink适用于观察嵌套对象(对象的属性是对象)属性的变化,如比如二维数组、对象数组、嵌套类场景。
class Parent {
public parentId: number;
constructor(parentId: number) {
this.parentId = parentId;
}
getParentId(): number {
return this.parentId;
}
setParentId(parentId: number): void {
this.parentId = parentId;
}
}
[@Observed](/user/Observed)
class Child {
public childId: number;
constructor(childId: number) {
this.childId = childId;
}
getChildId(): number {
return this.childId;
}
setChildId(childId: number): void {
this.childId = childId;
}
}
class Cousin extends Parent {
public cousinId: number = 47;
public child: Child;
constructor(parentId: number, cousinId: number, childId: number) {
super(parentId);
this.cousinId = cousinId;
this.child = new Child(childId);
}
getCousinId(): number {
return this.cousinId;
}
setCousinId(cousinId: number): void {
this.cousinId = cousinId;
}
getChild(): number {
return this.child.getChildId();
}
setChild(childId: number): void {
this.child.setChildId(childId);
}
}
@Component
struct ViewChild {
[@ObjectLink](/user/ObjectLink) child: Child;
build() {
Column({ space: 10 }) {
Text(`childId: ${this.child.getChildId()}`)
Button('Change childId')
.onClick(() => {
this.child.setChildId(this.child.getChildId() + 1);
})
}
}
}
@Entry
@Component
struct MyView {
@State cousin: Cousin = new Cousin(10, 20, 30);
build() {
Column({ space: 10 }) {
Text(`parentId: ${this.cousin.parentId}`)
Button('Change Parent.parentId')
.onClick(() => {
this.cousin.parentId += 1;
})
Text(`cousinId: ${this.cousin.cousinId}`)
Button('Change Cousin.cousinId')
.onClick(() => {
this.cousin.cousinId += 1;
})
ViewChild({ child: this.cousin.child }) // Text(`childId: ${this.cousin.child.childId}`)的替代写法
Button('Change Cousin.Child.childId')
.onClick(() => {
this.cousin.child.childId += 1;
})
}
}
}
如果Child 使用@ObjectLink,Child必须传参数,且不能初始化变量;
如果Child 使用@State,Child参数可传可不传。
@ObjectLink 用于在父子组件间共享对象类型状态变量,子组件通过该装饰器直接引用父组件对象,实现属性级双向同步。它不创建副本,父组件对象属性变化时会触发子组件更新。适用于需要子组件修改对象内部属性并同步回父组件的场景。
@Observed
class User {
name: string = ''
constructor(name: string) { this.name = name }
}
@Entry
@Component
struct Parent {
@State user: User = new User('Tom')
build() {
Column() {
Text('父: ' + this.user.name)
Button('父改为Jerry')
.onClick(() => {
// 整体替换对象
this.user = new User('Jerry')
})
// 传入子组件,子组件需同步此替换
Child({ u: this.user })
}
}
}
@Component
struct Child {
// 用 @ObjectLink 接收,不能初始化,且必须用 $ 语法
@ObjectLink u: User
build() {
Column() {
Text('子: ' + this.u.name)
Button('子改名')
.onClick(() => {
this.u.name = 'Spike' // 同步回父
})
}
}
}

