装饰器@State、@Link、@Prop
- @State装饰的变量是组件内部的状态数据,当状态数据被修改时,会调用所有组件的build()方式进行UI刷新;
- @Link装饰的变量可以和父组件的@State变量进行双向的数据绑定;
- @Prop装饰的变量和父组件的@State变量进行单项绑定,父组件@State变量改变,子组件的@Prop变量会改变,反之子组件的@Prop变量改变父组件不做改变;
小试牛刀:
任务管理列表实现:页面视图顶部是任务进度(同步任务状态)、视图下部分是卡片任务列表(任务的增加/修改状态)
-
卡片样式
//卡片
@Styles function Card(){
.width('90%')
.margin(15)
.backgroundColor(Color.White)
.borderRadius(15)
.shadow({radius:5,offsetY:5,offsetX:3,color:'#000000'})
}
-
任务/状态模型封装
//任务
class Task{
//任务id
static id:number = 1
//任务名称
name:string = '任务'+(Task.id++).toString()
//是否完成
isFinished:boolean = false
}
//任务进度
class TaskStatus{
finishedCount:number = 0
totalCount:number = 0
}
-
页面构建
//页面入口
@Entry
@Component
struct IndexPage {
@State status:TaskStatus = new TaskStatus()
// @Provide status:TaskStatus = new TaskStatus()
build(){
Column(){
//顶部进度
ProgressTaskView({status:$status})
// ProgressTaskView()
//任务列表
ListTaskView({status:$status})
// ListTaskView()
}.backgroundColor('#eeeeee')
}
}
-
Component:任务进度、任务列表
//任务进度
@Component
struct ProgressTaskView{
@Link status:TaskStatus
build(){
Column(){
Row(){
Text('任务进度:')
.fontColor('#333333')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Row(){
Text(this.status.finishedCount.toString())
.fontColor('#333333')
.fontSize(14)
.fontWeight(FontWeight.Medium)
Text('/'+this.status.totalCount.toString())
.fontColor('#333333')
.fontSize(14)
.fontWeight(FontWeight.Medium)
}
}.justifyContent(FlexAlign.SpaceEvenly)
.width('100%')
}
.height('150')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.Card()
}
}
//任务列表
@Component
struct ListTaskView{
@Link status:TaskStatus
@State listData:Task[] = [
]
build(){
Column(){
//添加任务
Row(){
Button({type:ButtonType.Normal}){
Text('添加任务')
.fontColor(Color.White)
.fontSize(14)
.fontWeight(FontWeight.Medium)
}.backgroundColor(Color.Blue)
.width(150)
.height(50)
.borderRadius(25)
.onClick(()=>{
console.log('添加任务点击')
this.listData.push(new Task())
this.status.totalCount = this.listData.length
console.log('添加任务点击'+this.listData)
})
}.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)
//任务列表
List({space:0,initialIndex:0}){
ForEach(this.listData,(item:Task)=>{
ListItem(){
Row(){
Text(item.name)
.fontColor('#333333')
.fontSize(14)
.fontWeight(FontWeight.Medium)
Checkbox().onChange((value)=>{
item.isFinished = value
this.status.finishedCount = this.listData.filter(item =>item.isFinished).length
}).select(item.isFinished)
}.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.padding({left:20,right:20})
.width('100%')
}.height(60)
.Card()
.backgroundColor(item.isFinished?'#eeefff':Color.White)
})
}.listDirection(Axis.Vertical)
.cachedCount(this.listData.length)
}.layoutWeight(1)
}
}
@Provide、@Consume
@Provide、@Consume提供跨组件数据状态双向同步,适用于多组件多模块的场景
小试牛刀:
还是上面的例子,我们用@Provide、@Consume来实现
-
页面构建调整
@Entry
@Component
struct IndexPage {
@Provide status:TaskStatus = new TaskStatus()
build(){
Column(){
//顶部进度
ProgressTaskView()
//任务列表
ListTaskView()
}.backgroundColor('#eeeeee')
}
}
-
Component调整
ProgressTaskView组件和ListTaskView组件的@Link status:TaskStatus变量换成@Consume status:TaskStatus,然后Run一下看看实际效果
-
与@State、@Link、@Prop区别
-
@Provide、@Consume不需要传参数,可以理解为生产者/消费者模式的通知,内存开销相比较大;
-
子组件@Prop变量实际上是父组件@State变量的拷贝;
-
子组件@Link变量实际上和父组件@State变量共用一个内存空间,用法类似C/C++函数传地址的方式&($);
@ObjectLink、@Observed
@ObjectLink、@Observed是可以解决模型嵌套(A模型中含B模型,B模型变量状态改变)或数组元素是复杂模型的情况的状态管理数据同步
小试牛刀
依然使用上面的任务列表,完成的任务与未完成的任务做一个背景区分,需要把List的ListItem拆分出来使用;参数item:Task加上@ObjectLink ,Task对象加上@Observed声明,然后CheckBox状态改变,背景就会同步改变;onTap给List组件同步完成任务数使用;
//ListItem
@Component
struct ItemView{
index:number
@ObjectLink item:Task
onTap:()=>void
build(){
Row(){
Text(this.item.name+this.index.toString())
.fontColor('#333333')
.fontSize(14)
.fontWeight(FontWeight.Medium)
Checkbox().onChange((value)=>{
this.item.isFinished = value
this.onTap()
// this.status.finishedCount = this.listData.filter(item =>item.isFinished).length
}).select(this.item.isFinished)
}.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.padding({left:20,right:20})
.width('100%')
.height(60)
.Card()
.backgroundColor(this.item.isFinished?'#eeefff':Color.White)
}
}
@Styles function Card(){
.width('90%')
.margin(15)
.backgroundColor(Color.White)
.borderRadius(15)
.shadow({radius:5,offsetY:5,offsetX:3,color:'#000000'})
}
Gitee:gitee.com/winvsmary/h…