当父子组件之间需要数据同步的时候,可以使用@Prop和@Link装饰器。
实现的案例之中,代码时平铺直叙的,阅读性可理解性比较差。我们应改遵循组件化开发的思想。
在我们使用组件开发的时候,遇到数据同步问题的时候,@State状态是解决不了的。所以就要用到@Prop和@Link
我们可以将任务进度卡片封装为一个组件、将任务按钮和卡片列表封装为另外一个组件,来实现组件化的目的。?
我们先用@State尝试着封装任务进度卡片组件,然后再入口函数中使用。
@Component
struct TaskStatistics {
@State finishTask: number = 0
@State totalTask: number = 0
build(){
Row(){
Text("任务进度")
.fontSize(30)
.fontWeight(FontWeight.Bold)
Stack(){
Progress({
value:this.finishTask,
total:this.totalTask,
type:ProgressType.Ring
})
.width(100)
Row(){
Text(this.finishTask.toString())
.fontSize(24)
.fontColor('#36d')
Text(' / ' +this.totalTask.toString())
.fontSize(24)
}
}
}
.card()
.margin({top:20,bottom:10})
.justifyContent(FlexAlign.SpaceEvenly)
}
}
这样一来??PropPage组件就是父组件、TaskStatistics就是子组件。
其中的任务数量是由PropPage来维护的。所以我们需要从父亲向子传递数量属性,这时候我们会发现报错了,提示@State不支持之类的。
?这说明再父子传值的时候,@State状态已经不支持了。我们这个时候可以使用Prop和Link两个装时期。
@Prop | @Link | |
同步类型 | 单向同步 | 双向同步 |
允许装饰的变量类型 | 1.@Prop只支持string、number、boolean、enum类型 2.父组件对象类型、子组件对象属性 3.不可以是数组、any | 1.父子类型一致:string、number、boolean、enum、object、class,以及他们的数组 2.数组中元素增、删、替换会引起刷新 3.嵌套类型以及数组中的对象属性无法触发视图更新。 |
初始化方式 | 不允许子组件初始化 | 父组件传递,禁止子组件初始化 |
@Prop是单向同步,父组件的改变会立即传递给子组件,但是子组件的修改不会影响父组件,他传递的是拷贝的值。
@LInk是双向同步,传递的是变量的引用,父亲和儿子使用的是一个变量,所以两方都会感知到变量的改变。
在封装任务列表的时候,要注意一些问题,比如说build函数下不能放入多个顶层,所以我们加一个Column容器,我们将按钮函数也加入。因为任务列表同任务进度是同步更新数据的,这里我们使用Link状态来完成。
@Component
struct TaskList {
// 任务数量
@State tasks: Task[] = []
// 完成任务列表
@Link finishTask: number
// 所有的任务列表
@Link totalTask: number
// 通过过滤方法 更新已完成的任务数量
handleTaskChange(){
// 更新任务总数量
this.totalTask = this.tasks.length
// 已经完成的任务数量
this.finishTask = this.tasks.filter(item => item.finished).length
}
build(){
Column(){
// 2.新增任务按钮
Button('新增任务')
.width(200)
.onClick(()=>{
// 1.新增任务
this.tasks.push(new Task())
// 2.更新任务数组
this.totalTask = this.tasks.length
})
// 3.卡片列表
List({space:10}){
ForEach(
this.tasks,
(item: Task,index)=>{
ListItem(){
Row(){
Text(item.name)
.fontSize(20)
Checkbox()
.select(item.finished)
.onChange(val=>{
item.finished = val
// 通过过滤方法 更新已完成的任务数量
this.handleTaskChange()
})
}
.card()
.justifyContent(FlexAlign.SpaceBetween)
}
.swipeAction({end:this.DeleteButton(index)})
}
)
}
.width('100%')
.layoutWeight(1)
.alignListItem(ListItemAlign.Center)
}
}
@Builder DeleteButton(index: number){
Button("删除")
.onClick(()=>{
this.tasks.splice(index,1)
this.handleTaskChange()
})
}
}
我们在使用Link的时候,父类里面穿参数,需要使用$语法。?
我们会发现效果与我们最开始的效果是一样一样的。
我们来验证一下@Prop和@Link的数据类型
我们来验证是否支持对象属性
// 任务统计信息
class StaticInfo{
totalTask: number = 0
finishTask: number = 0
}
父组件中直接实例化?传值直接传入属性
我们在组件中使用对象属性来完成
struct TaskList {
// 任务数量
@State tasks: Task[] = []
@Link stat: StaticInfo
// 通过过滤方法 更新已完成的任务数量
handleTaskChange(){
// 更新任务总数量
this.stat.totalTask = this.tasks.length
// 已经完成的任务数量
this.stat.finishTask = this.tasks.filter(item => item.finished).length
}
?
@Provide和@Consume可以跨组件提供类似@State和@Link的双向同步功能。
@Provide和@Consume内部就能传递信息,不需要手写传递。?
我们可以直接使用provide和consume,参数就不需要传递了,内部帮忙维护,代价是资源的损耗。所以我们在使用的时候优先考虑@State和@Link状态,遇到跨组件的时候,在使用@Provide和@Consume。
?