0
点赞
收藏
分享

微信扫一扫

浅析ArkUI之@ObservedLink与@Link

本文正在参加星光计划3.0–夏日挑战赛

前言

@Link和@ObservedLink都是用来双向同步父组件和子组件的状态,那它们有什么区别呢?本文做一些浅析比较他们的使用场景和区别以及使用@ObservedLink注意事项,起到抛砖引玉的效果

@Link与@ObservedLink的使用场景

@Link

@Link往往与@State搭配使用,前者在子组件,后者在父组件

@Entry
@Component
struct Player {
@State isPlaying: boolean = false
build() {
Column() {
PlayButton({buttonPlaying: $isPlaying})
Text(`Player is ${this.isPlaying? '':'not'} playing`)
}
}
}

@Component
struct PlayButton {
@Link buttonPlaying: boolean
build() {
Column() {
Button() {
Image(this.buttonPlaying? 'play.png' : 'pause.png')
}.onClick(() => {
this.buttonPlaying = !this.buttonPlaying
})
}
}
}
  • 解释:Player作为父组件持有@State变量isPlaying,PlayButton作为子组件持有@Link的变量buttonPlaying,两个变量指向同一个对象。@Link修饰的变量更像C语言的指针

@ObservedLink

@Link和@ObservedLink很类似,都可以理解成一个指针,子组件的对象指向父组件的对象,但是什么时候用@Link,什么时候用@ObservedLink呢?

官方解释给出了一大段说明引入的动机,我看的比较懵逼,有理解的小伙伴不妨评论解释下。按照我的理解是单个变量用@Link,一组变量用@ObservedLink

注:@Link也可以修饰自定义的class

@Link针对于单个变量

class CircleBean {
radius: number
offsetX: number = 0
offsetY: number = 0
}

@Entry
@Component
struct Index {
@State bean: CircleBean = new CircleBean()

build() {
Stack() {
MyCircle({ bean: $bean, onTouch: this.onBeanTouch.bind(this) })
}
.width('100%')
.height('100%')
}
}

@Component
struct MyCircle {
@Link bean: CircleBean

build() {
Circle()
.size({ width: this.bean.radius, height: this.bean.radius })
.offset({ x: this.bean.offsetX, y: this.bean.offsetY })
.backgroundColor("#ff0")
}
}
  • 解释:
    1. 如上我们定义了一个CircleBean类,保存圆的信息
    2. Index作为父组件定义了@State变量bean
    3. MyCircle作为子组件定义了@Link变量bean,指向父组件的@State变量

@ObservedLink用来渲染一组对象

如果是用到Foreach的情况下就需要用到@ObservedLink,因为@Link需要一个@State的变量来连接,而我们需要渲染一堆组件,往往把这一对数据放到一个集合中,然后使用ForEach,这时就不能使用@Link而用@ObservedLink了

@Observed
class CircleBean {
radius: number = 0
offsetX: number = 0
offsetY: number = 0
}

@Entry
@Component
struct Index {
@State beans: Array<CircleBean> = new Array<CircleBean>()

onPageShow() {
let offset = 0
for (let i = 0;i < 3; i++) {
let b = new CircleBean()
b.radius = (i + 1) * 50
b.offsetX = 0
offset += b.radius
b.offsetY = offset
this.beans.push(b)
}
}

build() {
Stack() {
ForEach(this.beans, (b) => {
MyCircle({ bean: b, onTouch: this.onBeanTouch.bind(this) })
})
}
.width('100%')
.height('100%')
}
}

@Component
struct MyCircle {
@ObjectLink bean: CircleBean

build() {
Circle()
.size({ width: this.bean.radius, height: this.bean.radius })
.offset({ x: this.bean.offsetX, y: this.bean.offsetY })
.backgroundColor("#ff0")
}
}
  • 解释

    1. 在上面的基础上我们使用的@Observed来修饰CircleBean
    2. 在父组件Index中我们定义了@State的集合变量beans
    3. 在父组件Index的build中我们使用ForEach来创建MyCircle组件
    4. 在MyCircle中我们使用ObjectLink来接收,有人会说这里换成@Link行不行,不行,因为ForEach哪里参数b是局部变量,而不是@State修饰的成员变量
  • 总结:@Link适用于单个对象,@ObservedLinked适用于集合对象

@ObservedLinked的副作用

子类继承被@Observed过的父类不能再添加@Observed

@Observed
class CircleBean {
radius: number
}

@Observed
class RingBean extends CircleBean {
thickness: number
}

image20220726202218104.png

  • 解释:如上,CircleBean被@Observed修饰后,那么其子类RingBean不能再被@Observed修饰了,否则抛出如上异常

被observed修饰的类的不能使用instanceof判断对象类型

@Observed
class CircleBean {
radius: number
}

class RingBean extends CircleBean {
thickness: number
}

let b = new CircleBean()
console.log("b instanceof CircleBean:"+(b instanceof CircleBean)) //false
b = new RingBean()
console.log("b instanceof CircleBean:"+(b instanceof CircleBean)) //false
console.log("b instanceof CircleBean:"+(b instanceof RingBean)) //false
  • 解释:CircleBean被@Observed修饰后,不能通过instanceof来判断对象b的类型,如上打印都是false

被observed修饰的类的对象不支持继承

@Observed
class CircleBean {
radius: number

fun1(){
console.log("fun1")
}
}

class RingBean extends CircleBean {
thickness: number

fun2(){
console.log("fun2")
}
}

let b = new CircleBean()
b.fun1()
let rb: RingBean = new RingBean()
rb.fun2()

image20220726203810147.png

  • 解释:上诉代码虽然编译成功,但是运行时会第20行代码抛出异常,找不到fun2()这个方法

静态方法不能使用

@Observed
class CircleBean {
radius: number

static print() {
console.log('print')
}
}

@Entry
@Component
struct Index {
onPageShow() {
RingBean.print()
}
}

image20220726202753659.png

  • 分析:在上面的14行,调用print方法就会抛出一个异常TypeError: not a function

总结:Observed会影响类的继承,类的静态方法

状态变量不要通过传递参数方式修改

  • 不要通过传参的方式去改状态变量
@Component
struct MyCircle {
@ObjectLink bean: CircleBean
build() {
Circle()
.size({ width: this.bean.radius, height: this.bean.radius })
.onTouch(e => {
this.onBeanTouch(this.bean, e)
this.onBeanTouch2(e)
})
.offset({ x: this.bean.offsetX, y: this.bean.offsetY })
.backgroundColor("#ff0")
}

onBeanTouch(b: CircleBean, e: TouchEvent) {
console.log("b==this.bean:"+(b==this.bean)+",b====this.bean:"+(b===this.bean))
console.log("onBeanTouch:" + b.offsetX + "," + b.offsetY + ",type:" + e.type)
switch (e.type) {
case TouchType.Down: {
this.screenX = e.touches[0].screenX
this.screenY = e.touches[0].screenY
break
}

case TouchType.Move: {
let x = e.touches[0].screenX - this.screenX
let y = e.touches[0].screenY - this.screenY
b.offsetX += x;
b.offsetY += y;
break
}
case TouchType.Up: {
let x = e.touches[0].screenX - this.screenX
let y = e.touches[0].screenY - this.screenY
b.offsetX += x;
b.offsetY += y;
break
}
}
this.screenX = e.touches[0].screenX
this.screenY = e.touches[0].screenY
}

解释:Line8调用onBeanTouch时将this.bean作为形参传递过去,并在onBeanTouch中用b来代替,经过测试对b的内容进行修改不会引起重绘,经过打印比较b和this.bean,无论比较内容还是比较地址都是false,我们可以得出函数调用会改变我们的对象

  • 可以使用局部变量来引用状态变量,对局部变量的修改会引起重绘
onBeanTouch2(e: TouchEvent) {
let b = this.bean
console.log("b==this.bean:"+(b==this.bean)+",b====this.bean:"+(b===this.bean))
console.log("onBeanTouch:" + b.offsetX + "," + b.offsetY + ",type:" + e.type)
switch (e.type) {
case TouchType.Down: {
this.screenX = e.touches[0].screenX
this.screenY = e.touches[0].screenY
break
}

case TouchType.Move: {
let x = e.touches[0].screenX - this.screenX
let y = e.touches[0].screenY - this.screenY
b.offsetX += x;
b.offsetY += y;
break
}
case TouchType.Up: {
let x = e.touches[0].screenX - this.screenX
let y = e.touches[0].screenY - this.screenY
b.offsetX += x;
b.offsetY += y;
break
}
}
this.screenX = e.touches[0].screenX
this.screenY = e.touches[0].screenY
}

附件链接:https://ost.51cto.com/resource/2224

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz

举报

相关推荐

0 条评论