鸿蒙应用开发-组件组件状态共享(Prop、Link...)

状态共享-父子单向

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

java 复制代码
@Entry
@Component
struct Index {

  @State
  money: number = 0

  build() {
    Column({ space: 20 }){
      Text('父组件:' + this.money)
        .onClick(() => {
          this.money ++
        })
      Child({ money: this.money })
    }
    .width('100%')
    .height('100%')
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct Child {

  @Prop
  money: number

  build() {
    Text('子组件:' + this.money)
      .onClick(() => {
        this.money ++
      })
  }
}
  • 支持类型 string、number、boolean、enum 类型
  • 子组件可修改 Prop 数据值,但不同步到父组件,父组件更新后覆盖子组件 Prop 数据
  • 子组件可以初始化默认值,注意:目前编译器会提示错误,请忽略,下个版本将修复

状态共享-父子双向

子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。

1)简单类型 stringnumberbooleanenum

java 复制代码
@Entry
@Component
struct Index {

  @State
  money: number = 0

  build() {
    Column({ space: 20 }){
      Text('父组件:' + this.money)
        .onClick(() => {
          this.money ++
        })
      Child({ money: $money })
    }
    .width('100%')
    .height('100%')
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct Child {

  @Link
  money: number

  build() {
    Text('子组件:' + this.money)
      .onClick(() => {
        this.money ++
      })
  }
}

2)复杂类型 Objectclass

java 复制代码
class Person {
  name: string
  age: number
}

@Entry
@Component
struct Index {

  @State
  person: Person = { name: 'jack', age: 18 }

  build() {
    Column({ space: 20 }){
      Text('父组件:' + `${this.person.name} 今年 ${ this.person.age } 岁`)
        .onClick(() => {
          this.person.age ++
        })
      Child({ person: $person })
    }
    .width('100%')
    .height('100%')
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct Child {

  @Link
  person: Person

  build() {
    Text('子组件:' +  `${this.person.name} 今年 ${ this.person.age } 岁`)
      .onClick(() => {
        this.person.age ++
      })
  }
}
  • 父组件传值的时候需要 **this.**改成 $ ,子组件 @Link 修饰数据

3. 状态共享-后代组件

@Provide@Consume ,应用于与后代组件的双向数据同步 ,应用于状态数据在多个层级之间传递的场景。

1)通过相同的变量名绑定

java 复制代码
@Entry
@Component
struct Index {
  @Provide
  money: number = 0

  build() {
    Column({ space: 20 }) {
      Text('父组件:' + this.money)
        .onClick(() => {
          this.money++
        })
      Parent()
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct Parent {
  @Consume
  money: number

  build() {
    Column({ space: 20 }) {
      Text('父组件:' + this.money)
        .onClick(() => {
          this.money++
        })
      Child()
    }
  }
}

@Component
struct Child {
  @Consume
  money: number

  build() {
    Text('子组件:' + this.money)
      .onClick(() => {
        this.money++
      })
  }
}

Object、class、string、number、boolean、enum 类型均支持

通过相同的变量别名绑定 @Provide('key') 和 @Consume('key') key需要保持一致

4. 状态共享-状态监听器

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

  • @State@Prop@Link 等装饰器在 @Watch 装饰之前
java 复制代码
import promptAction from '@ohos.promptAction'

@Component
struct Child {
  @Prop
  @Watch('onActiveIndex')
  activeIndex: number

  onActiveIndex() {
    promptAction.showToast({ message: '监听变化' })
  }

  build() {
    Column() {
      Text('Child' + this.activeIndex)
    }
  }
}

@Entry
@Component
struct Index {
  @State activeIndex: number = 0

  onChange (index: number) {
    this.activeIndex = index
    promptAction.showToast({ message: '点击' })
  }

  build() {
    Navigation() {
      Child({ activeIndex: this.activeIndex })
    }.toolBar({
      items: [
        { value: '首页', action: () => this.onChange(0) },
        { value: '我的', action: () => this.onChange(1) },
      ]
    })
  }
}
  • 在第一次初始化的时候,@Watch装饰的方法不会被调用

之前我们通过 赋值的方式 修改嵌套对象对象数组 这类复杂数据来更新UI,会影响对象对应所有UI更新; 通过 @Observed@ObjectLink 可以优化这个问题;

使用步骤:

  • 类 class 数据模拟需要定义通过构造函数,使用 @Observed 修饰这个类

  • 初始化数据:需要通过初始化构造函数的方式添加

  • 通过 @ObjectLink 关联对象,可以直接修改被关联对象来更新UI

需求:改造下知乎评论案例

1)定义构造函数和使用@Observed 修饰符,以及初始化数据

models/index.ets

java 复制代码
@Observed
export class ReplyItem {
  id: number
  avatar: string | Resource
  author: string
  content: string
  time: string
  area: string
  likeNum: number
  likeFlag?: boolean

  constructor(item: ReplyItem) {
    for (const key in item) {
      this[key] = item[key]
    }
  }
}

export const replyList: ReplyItem[] = [
  new ReplyItem({
    id: 1,
    avatar: 'https://picx.zhimg.com/027729d02bdf060e24973c3726fea9da_l.jpg?source=06d4cd63',
    author: '偏执狂-妄想家',
    content: '更何况还分到一个摩洛哥[惊喜]',
    time: '11-30',
    area: '海南',
    likeNum: 34
  }),
  new ReplyItem({
    id: 2,
    avatar: 'https://pic1.zhimg.com/v2-5a3f5190369ae59c12bee33abfe0c5cc_xl.jpg?source=32738c0c',
    author: 'William',
    content: '当年希腊可是把1:0发挥到极致了',
    time: '11-29',
    area: '北京',
    likeNum: 58
  }),
  new ReplyItem({
    id: 3,
    avatar: 'https://picx.zhimg.com/v2-e6f4605c16e4378572a96dad7eaaf2b0_l.jpg?source=06d4cd63',
    author: 'Andy Garcia',
    content: '欧洲杯其实16队球队打正赛已经差不多,24队打正赛意味着正赛阶段在小组赛一样有弱队。',
    time: '11-28',
    area: '上海',
    likeNum: 10
  }),
  new ReplyItem({
    id: 4,
    avatar: 'https://picx.zhimg.com/v2-53e7cf84228e26f419d924c2bf8d5d70_l.jpg?source=06d4cd63',
    author: '正宗好鱼头',
    content: '确实眼红啊,亚洲就没这种球队,让中国队刷',
    time: '11-27',
    area: '香港',
    likeNum: 139
  }),
  new ReplyItem({
    id: 5,
    avatar: 'https://pic1.zhimg.com/v2-eeddfaae049df2a407ff37540894c8ce_l.jpg?source=06d4cd63',
    author: '柱子哥',
    content: '我是支持扩大的,亚洲杯欧洲杯扩到32队,世界杯扩到64队才是好的,世界上有超过200支队伍,欧洲区55支队伍,亚洲区47支队伍,即使如此也就六成出现率',
    time: '11-27',
    area: '旧金山',
    likeNum: 29
  }),
  new ReplyItem({
    id: 6,
    avatar: 'https://picx.zhimg.com/v2-fab3da929232ae911e92bf8137d11f3a_l.jpg?source=06d4cd63',
    author: '飞轩逸',
    content: '禁止欧洲杯扩军之前,应该先禁止世界杯扩军,或者至少把亚洲名额一半给欧洲。',
    time: '11-26',
    area: '里约',
    likeNum: 100
  })
]

2)嵌套的对象,或者数组中的对象,传入子组件,组件使用 @ObjectLink 修饰符获取数据

pages/Index.ets

java 复制代码
import promptAction from '@ohos.promptAction'
import { ReplyItem, replyList } from '../models'


@Entry
@Component
struct Index {
  @State
  replyList: ReplyItem[] = replyList
  @State
  content: string = ''

  onReply() {
    const reply: ReplyItem = new ReplyItem({
      id: Math.random(),
      content: this.content,
      author: 'Zhousg',
      avatar: $r('app.media.avatar'),
      time: '12-01',
      likeNum: 0,
      area: '北京'
    })
    this.replyList.unshift(reply)
    this.content = ''
    promptAction.showToast({ message: '回复成功' })
  }

  build() {
    Stack() {
      Scroll() {
        Column() {
          NavComp()
          CommentComp()
          // space
          Divider()
            .strokeWidth(8)
            .color('#f5f5f5')
          // reply
          Column() {
            Text('回复 7')
              .width('100%')
              .margin({ bottom: 15 })
              .fontWeight(500)
            ForEach(this.replyList, (item: ReplyItem) => {
              ReplyComp({ item })
            })
          }
          .padding({ left: 15, right: 15, top: 15 })
        }
      }
      .padding({ bottom: 50 })

      Row() {
        TextInput({ placeholder: '回复~', text: this.content })
          .placeholderColor('#c3c4c5')
          .layoutWeight(1)
          .onChange((value) => {
            this.content = value
          })
        Text('发布')
          .fontSize(14)
          .fontColor('#09f')
          .margin({ left: 15 })
          .onClick(() => {
            this.onReply()
          })
      }
      .width('100%')
      .height(50)
      .padding({ left: 15, right: 15 })
      .position({ y: '100%' })
      .translate({ y: -50 })
      .backgroundColor('#fff')
      .border({ width: { top: 0.5 }, color: '#e4e4e4' })
    }

  }
}

@Component
struct ReplyComp {
  @ObjectLink
  item: ReplyItem

  onLike() {
    if (this.item.likeFlag) {
      this.item.likeNum--
      this.item.likeFlag = false
      promptAction.showToast({ message: '取消点赞' })
    } else {
      this.item.likeNum++
      this.item.likeFlag = true
      promptAction.showToast({ message: '点赞成功' })
    }
  }

  build() {
    Row() {
      Image(this.item.avatar)
        .width(32)
        .height(32)
        .borderRadius(16)
      Column() {
        Text(this.item.author)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 5 })
        Text(this.item.content)
          .margin({ bottom: 5 })
          .fontColor('#565656')
          .lineHeight(20)
        Row() {
          Text(`${this.item.time}•IP 属地${this.item.area}`)
            .layoutWeight(1)
            .fontSize(14)
            .fontColor('#c3c4c5')
          Row() {
            Image($r('app.media.heart'))
              .width(14)
              .height(14)
              .fillColor(this.item.likeFlag ? '#ff6600' : '#c3c4c5')
              .margin({ right: 4 })
            Text(this.item.likeNum.toString())
              .fontSize(14)
              .fontColor(this.item.likeFlag ? '#ff6600' : '#c3c4c5')
          }
          .onClick(() => {
            this.onLike()
          })
        }
      }
      .layoutWeight(1)
      .padding({ left: 10 })
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding({ bottom: 15 })
    .alignItems(VerticalAlign.Top)
  }
}

@Component
struct NavComp {
  build() {
    // nav
    Row() {
      Row() {
        Image($r('app.media.ic_public_arrow_left'))
          .width(12)
          .height(12)
          .fillColor('#848484')
      }
      .width(24)
      .height(24)
      .borderRadius(12)
      .backgroundColor('#f5f5f5')
      .justifyContent(FlexAlign.Center)
      .margin({ left: 13 })

      Text('评论回复')
        .padding({ right: 50 })
        .textAlign(TextAlign.Center)
        .fontSize(18)
        .layoutWeight(1)
    }
    .height(50)

  }
}

@Component
struct CommentComp {
  build() {
    // comment
    Row() {
      Image('https://picx.zhimg.com/1754b6bd9_xl.jpg?source=c885d018')
        .width(32)
        .height(32)
        .borderRadius(16)
      Column() {
        Text('欧洲足球锦标赛')
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 5 })
        Text('14-0!欧洲杯超级惨案+刷爆纪录!姆巴佩帽子戏法,法国7连胜,怎么评价这场比赛?')
          .margin({ bottom: 5 })
          .fontColor('#565656')
          .lineHeight(20)
        Row() {
          Text('10-21•IP 属地辽宁')
            .layoutWeight(1)
            .fontSize(14)
            .fontColor('#c3c4c5')
          Row() {
            Image($r('app.media.heart'))
              .width(14)
              .height(14)
              .fillColor('#c3c4c5')
              .margin({ right: 4 })
            Text('100')
              .fontSize(14)
              .fontColor('#c3c4c5')
          }
        }
      }
      .layoutWeight(1)
      .padding({ left: 10 })
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding({ left: 15, right: 15, bottom: 15 })
    .alignItems(VerticalAlign.Top)
  }
}

注意:

  • 对象需要通过构造函数初始化

  • 需要嵌套组件,因为需要使用 @ObjectLink

👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀

相关推荐
SameX4 小时前
HarmonyOS Next 安全生态构建与展望
前端·harmonyos
SameX4 小时前
HarmonyOS Next 打造智能家居安全系统实战
harmonyos
Random_index12 小时前
#Uniapp篇:支持纯血鸿蒙&发布&适配&UIUI
uni-app·harmonyos
鸿蒙自习室15 小时前
鸿蒙多线程开发——线程间数据通信对象02
ui·harmonyos·鸿蒙
SuperHeroWu717 小时前
【HarmonyOS】鸿蒙应用接入微博分享
华为·harmonyos·鸿蒙·微博·微博分享·微博sdk集成·sdk集成
zhangjr057520 小时前
【HarmonyOS Next】鸿蒙实用装饰器一览(一)
前端·harmonyos·arkts
诗歌难吟4641 天前
初识ArkUI
harmonyos
SameX1 天前
HarmonyOS Next 设备安全特性深度剖析学习
harmonyos
郭梧悠1 天前
HarmonyOS(57) UI性能优化
ui·性能优化·harmonyos
郝晨妤2 天前
鸿蒙原生应用开发元服务 元服务是什么?和App的关系?(保姆级步骤)
android·ios·华为od·华为·华为云·harmonyos·鸿蒙