【HarmonyOS】HarmonyOS NEXT学习日记:八、组件通信

【HarmonyOS】HarmonyOS NEXT学习日记:八、组件通信

通过前面的学习我们基本上掌握了如何封装组件,但是实际使用过程中组件之间的状态需要互相之间关联通讯,涉及到父子组件,后代组件之间的相互通信。
请添加图片描述

@State装饰器:组件内状态

这是最常用的一种装饰器,它装饰的变量不与父组件中任何类型的变量同步。
@State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。

@State装饰的变量拥有以下特点:

@State装饰的变量与子组件中的@Prop装饰变量之间建立单向数据同步,与@Link、@ObjectLink装饰变量之间建立双向数据同步。

@State装饰的变量生命周期与其所属自定义组件的生命周期相同。

@Prop装饰器:父子单向同步

@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。

代码示例:
我们在父组件定义一个title,将其传给子组件,子组件使用@Prop装饰的状态来接收
父组件:

// Index.ets
import text from '@ohos.graphics.text'
import { router } from '@kit.ArkUI';
import { MyListItem } from '../components/MyListItem'
@Entry
@Component
struct Index {
  @State title:string = 'parent'
  build() {
    Column(){
      Text('parentTitle:'+this.title)
        .fontSize(36)
        .fontColor(Color.Red)
      Text('childreTitle:').fontSize(36)
      MyListItem({title:this.title,onToDetail:()=>{
        router.pushUrl({url: 'pages/Second'})
      }})
    }
  }
}

子组件:

//MyListItem
@Component
export struct MyListItem {
  @Prop title: string
  onToDetail: Function = () => {
  }
  aboutToAppear(){
    this.title='children'
  }
  build() {
    Text(this.title).fontSize(50).onClick(() => {
      this.onToDetail()
    })
  }
}

在这里插入图片描述
可以看到,我们在子组件的aboutToAppear中将title修改为了’children‘,在子组件区域也确实展示为了children,但是父组件依然没有改变,是’parent‘。

那么我们修改父组件的title,子组件是什么表现呢?
修改父组件代码为:

// Index.ets
import text from '@ohos.graphics.text'
import { router } from '@kit.ArkUI';
import { MyListItem } from '../components/MyListItem'
@Entry
@Component
struct Index {
  @State title:string = 'parent'
  build() {
    Column(){
      Text('parentTitle:'+this.title)
        .fontSize(36)
        .fontColor(Color.Red)
        .onClick(()=>{
          this.title='parentChanged'
        })
      Text('childreTitle:').fontSize(36)
      MyListItem({title:this.title,onToDetail:()=>{
        router.pushUrl({url: 'pages/Second'})
      }})
    }
  }
}

页面初始化为
在这里插入图片描述

当我们点击文本触发新添加的点击事件修改父组件的title时:
在这里插入图片描述
子组件的@Prop title一起改变。

@Link装饰器:父子双向同步

子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。
我们还是写一个代码来看看效果

父组件:

// Index.ets
import text from '@ohos.graphics.text'
import { router } from '@kit.ArkUI';
import { MyListItem } from '../components/MyListItem'
@Entry
@Component
struct Index {
  @State title:string = 'parent'
  build() {
    Column(){
      Text('parentTitle:'+this.title)
        .fontSize(36)
        .fontColor(Color.Red)
        .onClick(()=>{
          this.title='parentChanged'
        })
      Text('childrenTitle:').fontSize(36)
      MyListItem({title:this.title})
    }
  }
}

子组件:

//MyListItem
@Component
export struct MyListItem {
  @Link title:string
  aboutToAppear(){
    console.log('title',this.title)
  }
  build() {
    Text(this.title).fontSize(50)
      .onClick(()=>{this.title='childrenChanged'})
  }
}

初始化页面
在这里插入图片描述
点击红色文字,修改父组件状态
在这里插入图片描述
点击最下方的黑色文字,修改子组件状态
在这里插入图片描述
至此,就实现了父子组件的状态同步通信。

问题

我们试着传递一个两层的对象看看
父组件:

// Index.ets
import text from '@ohos.graphics.text'
import { router } from '@kit.ArkUI';
import { MyListItem } from '../components/MyListItem'
import {childrenObj,title} from  '../components/MyListItem'
@Entry
@Component
struct Index {
  @State myTitle:title = {
    txt: 'parent'
  }
  @State obj:childrenObj = {
    title: this.myTitle
  }
  build() {
    Column(){
      Text('parentTitle:'+this.obj.title.txt)
        .fontSize(36)
        .fontColor(Color.Red)
        .onClick(()=>{
          console.log(this.obj.title.txt)
          this.obj.title.txt='parentChanged'
        })
      Text('childrenTitle:').fontSize(36)
      MyListItem({obj:this.obj})
    }
  }
}

子组件:

//MyListItem
interface childrenObj{
  title: title
}
interface title{
  txt: string
}
@Component
export struct MyListItem {
  @Link obj:childrenObj
  aboutToAppear(){
    console.log('title',this.obj.title.txt)
  }
  build() {
    Text(this.obj.title.txt).fontSize(50)
      .onClick(()=>{this.obj.title.txt='childrenChanged'})
  }
}
export {childrenObj,title}

在这里插入图片描述
!!!!!!!!!!!!无论如何修改obj.title.txt的值,页面都没有响应,这个状态对于页面来说,失去响应式了。
事实上在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。
如何解决我们稍后再说。!!!!!!!!!!!!!!

@Provide装饰器和@Consume装饰器:与后代组件双向同步

有的时候我们不仅仅需要父子之间传值,还需要后代组件和祖先组件之后跨层级传值,这种情况就需要用到 @Provide装饰器和@Consume装饰器。

@Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递。

其中@Provide装饰的变量是在祖先组件中,可以理解为被“提供”给后代的状态变量。@Consume装饰的变量是在后代组件中,去“消费(绑定)”祖先组件提供的变量。

我们在刚刚代码的基础上加一层组件Intermediate,让MyListItem组件变成Index下两层后代组件
Index:

// Index.ets
import text from '@ohos.graphics.text'
import { router } from '@kit.ArkUI';
import { Intermediate } from '../components/Intermediate'
@Entry
@Component
struct Index {
  @Provide title:string = 'parent'
  build() {
    Column(){
      Text('parentTitle:'+this.title)
        .fontSize(36)
        .fontColor(Color.Red)
        .onClick(()=>{
          this.title='parentChanged'
        })
      Text('childrenTitle:').fontSize(36)
      Intermediate()
    }
  }
}

Intermediate:

//Intermediate
import { MyListItem } from './MyListItem'

@Component
export struct Intermediate {
  build() {
    MyListItem()
  }
}

MyListItem:

//MyListItem
@Component
export struct MyListItem {
  @Consume title:string
  build() {
    Text(this.title).fontSize(50)
      .onClick(()=>{this.title='childrenChanged'})
  }
}

在这里插入图片描述
点击红色文字修改index状态
在这里插入图片描述
点击黑色文字修改后代组件状态
在这里插入图片描述
即完成了祖先组件和后代组件的双向状态绑定。

@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

好的现在我们来解决一下刚刚发现的问题,当对象嵌套两层之后,状态失去了响应性。

// Index.ets
//MyListItem
@Observed
class ClassA {
  public c: string;

  constructor(c: string) {
    this.c = c;
  }
}
@Observed
class ClassB {
  public a: ClassA;
  public b: string;

  constructor(a: ClassA, b: string) {
    this.a = a;
    this.b = b;
  }
}
@Component
struct MyListItem {
  @ObjectLink a:ClassA
  build() {
    Text(this.a.c).fontSize(50)
      .onClick(()=>{
        this.a.c = 'ca';
      })
  }
}
@Entry
@Component
struct Index {
  @State b: ClassB = new ClassB(new ClassA('a'),'b');
  build() {
    Column(){
      MyListItem({a:this.b.a})
    }
    .width('100%')
    .onClick(()=>{
      this.b.a.c='pa'
    })
  }
}

在这里插入图片描述
点击父元素,给b.a.c赋值’pa’
在这里插入图片描述
点击子组件给a.c 赋值 ‘ca’在这里插入图片描述

$$内置组件双向同步

很多原生的组件我们要双向同步状态可以使用 , , ,运算符为系统内置组件提供TS变量的引用,使得TS变量和系统内置组件的内部状态保持同步。

@Entry
@Component
struct TextInputExample {
  @State text: string = ''
  controller: TextInputController = new TextInputController()

  build() {
    Column({ space: 20 }) {
      Text(this.text)
      TextInput({ text: $$this.text, placeholder: 'input your word...', controller: this.controller })
        .placeholderColor(Color.Grey)
        .placeholderFont({ size: 14, weight: 400 })
        .caretColor(Color.Blue)
        .width(300)
    }.width('100%').height('100%').justifyContent(FlexAlign.Center)
  }
}

在这里插入图片描述
可以观察到输入框绑定值改变时,ui上的相同变量也一起刷新了。

@Watch装饰器:状态变量更改通知

@Watch应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变,可以使用@Watch为状态变量设置回调函数。

@Component
struct TotalView {
  @Prop @Watch('onCountUpdated') count: number = 0;
  @State total: number = 0;
  // @Watch 回调
  onCountUpdated(propName: string): void {
    this.total += this.count;
  }

  build() {
    Text(`Total: ${this.total}`)
  }
}

@Entry
@Component
struct CountModifier {
  @State count: number = 0;

  build() {
    Column() {
      Button('add to basket')
        .onClick(() => {
          this.count++
        })
      TotalView({ count: this.count })
    }
  }
}

在这里插入图片描述

当子组件接收的count变化时会触发watch的回调onCountUpdated,在其中修改count状态的的值

  • 27
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值