一、用法
在父子组件需要进行数据同步的时候,可以通过@Prop和@Link装饰器来做到。在父组件中用@State装饰,在自组件中用@Prop或@Link装饰。
结论:@Prop用于子组件只监听父组件的数据改变而改变,自己不对数据改变
@Link用于子组件与父组件都会对数据改变,都需要在数据改变的时候发生相应的更新。
二、@Prop和@Link区别
2.1数据同步的类型不同
@Prop的数据是单向传递的,父组件改变能通知子组件,但是子组件改变不能通知父组件。
@Link的数据是双向传递的,父组件改变能通知子组件,子组件的改变也可以通知父组件。
2.2 装饰的变量类型不同
@Prop可以装饰的类型有限
- 只能支持string、number、boolean、enum类型。
- 如果父组件是对象类型,可以传递对象的属性,子组件可以内部可以是对象的属性。
- 不可以是数组、any类型
@Link可以装饰的类型较多
- 支持string、number、boolen、enum、object、数组等
- 数组的增、删、改都可以触发更新
- 嵌套类型以及数组中对象的属性无法触发更新,类似@State
2.3装饰后属性传递方式不同
@Prop装饰的属性不能初始化,因为传递方式是拷贝,从父组件中拷贝一份值给子组件。所以可以直接通过this.xxx传递。
@Link装饰的属性也不能初始化。传递方式是指针传递,需要用$修饰传递的属性。
三、实际案例
接上一篇state案例,我们在同一个组件中编写的代码,导致代码非常臃肿。那么,为了更加简洁,就对代码进行了拆分,将代码又拆分出来了2个组件TaskStitics(任务进度卡片)和StaticList(任务列表卡片)。
其中,TaskStitics组件只接收数据的改变,不会对数据改变,所以采用单向同步的@Prop装饰
StaticList组件因为可以对数据进行完成、删除等操作,也会对父组件的数据改变,所以采用双向同步的@Link装饰。
class Task {static id: number = 1name:string = '任务名称'+Task.id++finished:boolean = false
}@Styles function card() {.width('90%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({radius:6,color:'#1F00000',offsetX:2,offsetY:4})
}@Extend(Text) function tasksuccessed(finish: boolean) {.decoration({type: finish ? TextDecorationType.LineThrough : TextDecorationType.None}).fontColor(finish? '#B1B2B1': Color.Black)
}@Entry
@Component
struct ProgressTask {@State totalTasks: number = 0@State finishTasks:number = 0@State tasks: Task[] = []build() {Column() {//顶部任务进度卡片TaskStitics({ totalTasks: this.totalTasks, finishTasks: this.finishTasks })//任务列表+新增按钮StaticList({totalTasks:$totalTasks,finishTasks:$finishTasks,tasks:$tasks})}}
}@Component
struct TaskStitics {@Prop totalTasks: number@Prop finishTasks:numberbuild() {//进度卡片Row() {Text('任务进度:').fontWeight(FontWeight.Bold).fontSize(30).layoutWeight(1)Stack() {Progress({value:this.finishTasks,total:this.totalTasks,type:ProgressType.Ring}).width(100)Row() {Text(this.finishTasks.toString()).fontSize(24).fontColor('#36D')Text(' / '+this.totalTasks.toString()).fontSize(24)}}}.card().height(150).margin({top:20,bottom:10}).justifyContent(FlexAlign.SpaceEvenly)}
}@Component
struct StaticList {@Link totalTasks: number@Link finishTasks:number@Link tasks: Task[]handleTaskNumber() {this.totalTasks = this.tasks.lengththis.finishTasks = this.tasks.filter(item =>item.finished).lengthconsole.log('完成任务数'+this.finishTasks)}@Builder deleteButton(index:number) {Button('➖').fontColor(Color.White).backgroundColor(Color.Red).width(40).height(40).type(ButtonType.Circle).margin({left:5}).onClick(() => {this.tasks.splice(index,1)this.handleTaskNumber()})}build() {Column() {//添加按钮Button('新增任务').width(200).height(35).onClick(() => {this.tasks.push(new Task())this.handleTaskNumber()}).margin({ bottom: 20 })//任务列表List({ space: 10 }) {ForEach(this.tasks, (task: Task, index) => {ListItem() {Row() {Text(task.name).fontSize(20).tasksuccessed(task.finished)Checkbox().select(task.finished).onChange(value => {task.finished = valueconsole.log('任务状态' + value + '')this.handleTaskNumber()})}.card().justifyContent(FlexAlign.SpaceBetween)}.swipeAction({ end: this.deleteButton(index) })}, item => '' + item.name)}.width('100%').alignListItem(ListItemAlign.Center).layoutWeight(1)}.width('100%').height('100%')}}
四、补充@Provide和@Consume
@Provide和@Consume可以跨组件提供类似于@State和@Link的双向同步。
设想有三级组件父组件->子组件->孙子组件。
如果想要父组件与孙子组件中的数据达到双向同步,那么就需要先父组件与子组件绑定,然后子组件与孙子组件绑定,需要绑定多次。这个时候就可以用@Provide和@Consume装饰器了。
在父组件中用@Provide装饰,在孙子组件中采用@Consume装饰,就可以实现双向数据同步。
需要注意的是,使用这两个装饰器的时候不需要在父组件中传递参数。
整体关系如下图