状态共享-父子单向
**@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)简单类型 string 、number 、boolean 、enum
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)复杂类型 Object 、class
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装饰的方法不会被调用
5. @Observed与@ObjectLink
之前我们通过 赋值的方式 修改嵌套对象 或对象数组 这类复杂数据来更新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老皮!!!欢迎大家来找我探讨交流👀