经历的越多,越喜欢简单的生活,干净的东西,清楚的感觉,有结果的事,和说到做到的人。把圈子变小,把语放缓,把心放宽,用心做好手边的事儿,该有的总会有的!
目录
一,定义
之前所讲的装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。
@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:
①被@Observed装饰的类,可以被观察到属性的变化;
②子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。
③单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。
限制条件:
①使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。
②@ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。
二,装饰器说明
@Observed类装饰器 | 说明 |
---|---|
装饰器参数 | 无 |
类装饰器 | 装饰class。需要放在class的定义前,使用new创建类对象。 |
@ObjectLink变量装饰器 | 说明 |
---|---|
装饰器参数 | 无 |
同步类型 | 不与父组件中的任何类型同步变量。 |
允许装饰的变量类型 | 必须为被@Observed装饰的class实例,必须指定类型。 不支持简单类型,可以使用@Prop。 支持继承Date或者Array的class实例 @ObjectLink的属性是可以改变的,但是变量的分配是不允许的,也就是说这个装饰器装饰变量是只读的,不能被改变。 |
被装饰变量的初始值 | 不允许。 |
!注意:
@ObjectLink装饰的变量不能被赋值,如果要使用赋值操作,请使用@Prop。
①@Prop装饰的变量和数据源的关系是单向同步,@Prop装饰的变量在本地拷贝了数据源,所以它允许本地更改,如果父组件中的数据源有更新,@Prop装饰的变量本地的修改将被覆盖;
②@ObjectLink装饰的变量和数据源的关系是双向同步,@ObjectLink装饰的变量相当于指向数据源的指针。禁止对@ObjectLink装饰的变量赋值,如果一旦发生@ObjectLink装饰的变量的赋值,则同步链将被打断。因为@ObjectLink修饰的变量通过数据源(Object)引用来初始化。对于实现双向数据同步的@ObjectLink,赋值相当于更新父组件中的数组项或者class的属性,TypeScript/JavaScript不能实现,会发生运行时报错。
三,变量的传递/访问规则说明
@ObjectLink传递/访问 | 说明 |
---|---|
从父组件初始化 | 必须指定。 初始化@ObjectLink装饰的变量必须同时满足以下场景: - 类型必须是@Observed装饰的class。 - 初始化的数值需要是数组项,或者class的属性。 - 同步源的class或者数组必须是@State,@Link,@Provide,@Consume或者@ObjectLink装饰的数据。 |
与源对象同步 | 双向。 |
可以初始化子组件 | 允许,可用于初始化常规变量、@State、@Link、@Prop、@Provide |
四,使用
注意,@Observed必须在ets文件中使用,在ts文件中不允许使用
1,简单对象的使用
用@Observed装饰数据类
TypeScript
@Observed
export default class YuanZhen {
public name: string = 'YuanZhen';
public age: number = 18;
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
子组件
TypeScript
import YuanZhen from './bean/YuanZhen'
@Component
export default struct ProvideTest {
@ObjectLink yuanZhen:YuanZhen
build() {
Row() {
Column() {
Text("子name:" + this.yuanZhen.name+"\nage:"+this.yuanZhen.age)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.yuanZhen.name ="袁震2"
this.yuanZhen.age=35
})
}.width('100%')
}.height('100%')
}
}
父组件
TypeScript
import YuanZhen from './bean/YuanZhen';
import ProvideTest from './ProvideTest';
@Entry
@Component
struct Index {
@State yuan:YuanZhen=new YuanZhen("袁震",18)
build() {
Column(){
Text("父name:" + this.yuan.name+"\nage:"+this.yuan.age)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.yuan.name ="袁震1"
this.yuan.age =20
})
ProvideTest({yuanZhen:this.yuan})
}
}
}
注意:这里会有一个报错,Assigning the '@State' decorated attribute 'yuan' to the '@ObjectLink' decorated attribute 'yuanZhen' is not allowed. <ArkTSCheck> 是编译器的问题,但是不影响使用
运行效果:
点击父点击子
所以, 被@Observed和@ObjectLink修饰的class可以实现父子双向绑定
2,嵌套对象的使用
用@Observed修饰的数据类:
TypeScript
@Observed
export default class YuanZhen {
public name: string = 'YuanZhen';
public age: number = 18;
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
TypeScript
import YuanZhen from './YuanZhen';
@Observed
export default class Yuan {
public number: number = 1;
public yuanZhen: YuanZhen = new YuanZhen('yuanzhen', 18);
constructor(number: number, yuanZhen: YuanZhen) {
this.number = number
this.yuanZhen = yuanZhen
}
}
子组件:
TypeScript
import YuanZhen from './bean/YuanZhen'
@Component
export default struct ProvideTest {
@ObjectLink yuanZhen:YuanZhen
build() {
Row() {
Column() {
Text("子name:" + this.yuanZhen.name+"\nage:"+this.yuanZhen.age)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.yuanZhen.name ="袁震2"
this.yuanZhen.age=35
})
}.width('100%')
}.height('100%')
}
}
父组件:
TypeScript
import Yuan from './bean/Yuan';
import YuanZhen from './bean/YuanZhen';
import ProvideTest from './ProvideTest';
@Entry
@Component
struct Index {
@State yuan:Yuan=new Yuan(1,new YuanZhen("袁震",18))
build() {
Column(){
Text("父name:" + this.yuan.yuanZhen.name+"\nage:"+this.yuan.yuanZhen.age)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.yuan.yuanZhen.name ="袁震1"
this.yuan.yuanZhen.age =20
console.info("yz----name="+this.yuan.yuanZhen.name )
})
ProvideTest({yuanZhen:this.yuan.yuanZhen})
}
}
}
运行:
点击父点击子
点击父组件,输出:
由此可见,当class为嵌套类型时,父组件的UI不会改变,但是数据会改变,子组件的UI和数据都会改变
3,数组对象的使用
子组件
TypeScript
import Yuan from './bean/Yuan'
@Component
export default struct ProvideTest {
@ObjectLink yuan:Yuan
build() {
Row() {
Column() {
Text("子name:"+this.yuan.yuanZhen.name+"\nage:"+this.yuan.yuanZhen.age+"\nnumber:"+this.yuan.number)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.yuan.yuanZhen.name ="袁震子组件"
this.yuan.yuanZhen.age=35
console.log("yz---name:"+this.yuan.yuanZhen.name)
})
}.width('100%')
}.height('100%')
}
}
父组件
TypeScript
import Yuan from './bean/Yuan';
import YuanZhen from './bean/YuanZhen';
import ProvideTest from './ProvideTest';
@Entry
@Component
struct Index {
@State yuan:Array<Yuan>=new Array
aboutToAppear(){
let yuan1:Yuan =new Yuan(1,new YuanZhen("袁震1",18))
let yuan2:Yuan =new Yuan(2,new YuanZhen("袁震2",19))
let yuan3:Yuan =new Yuan(3,new YuanZhen("袁震3",20))
this.yuan.push(yuan1)
this.yuan.push(yuan2)
this.yuan.push(yuan3)
}
build() {
Column(){
Text("父name:" + this.yuan[0].yuanZhen.name+"\nage:"+this.yuan[0].yuanZhen.age+"\nnumber:"+this.yuan[0].number)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.yuan[0].yuanZhen.name="袁震父组件"
this.yuan[0].number =10
this.yuan[0].yuanZhen.age=30
})
ProvideTest({yuan:this.yuan[0]})
}
}
}
运行:
点击父点击子
可以看到,父组件不会更新本身的UI,会更新子组件的UI,子组件既不会更新自己的UI也不会更新父组件的UI。
但是,可以直接传给子组件简单类,这样可以更新子组件
子组件
TypeScript
import YuanZhen from './bean/YuanZhen'
@Component
export default struct ProvideTest {
@ObjectLink yuanZhen:YuanZhen
build() {
Row() {
Column() {
Text("子name:"+this.yuanZhen.name+"\nage:"+this.yuanZhen.age)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.yuanZhen.name ="袁震子组件"
this.yuanZhen.age=35
console.log("yz---name:"+this.yuanZhen.name)
})
}.width('100%')
}.height('100%')
}
}
父组件
TypeScript
import Yuan from './bean/Yuan';
import YuanZhen from './bean/YuanZhen';
import ProvideTest from './ProvideTest';
@Entry
@Component
struct Index {
@State yuan:Array<Yuan>=new Array
aboutToAppear(){
let yuan1:Yuan =new Yuan(1,new YuanZhen("袁震1",18))
let yuan2:Yuan =new Yuan(2,new YuanZhen("袁震2",19))
let yuan3:Yuan =new Yuan(3,new YuanZhen("袁震3",20))
this.yuan.push(yuan1)
this.yuan.push(yuan2)
this.yuan.push(yuan3)
}
build() {
Column(){
Text("父name:" + this.yuan[0].yuanZhen.name+"\nage:"+this.yuan[0].yuanZhen.age+"\nnumber:"+this.yuan[0].number)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.yuan[0].yuanZhen.name="袁震父组件"
this.yuan[0].number =10
this.yuan[0].yuanZhen.age=30
})
ProvideTest({yuanZhen:this.yuan[0].yuanZhen})
}
}
}
点击父点击子
父组件的ui不会改变,子组件的UI会实时更新