当应用中父子组件需要数据同步时,可以使用@Prop和@Link装饰器
一个组件被引用时,引用其他组件的组件就是父组件、被引用的组件就是子组件。
由于@State装饰器无法执行父子之间的数据通信
- @Prop:单向同步、父组件中修改数据,会同步到子组件,但是子组件修改数据,父组件中不会同步,底层实现的是传递的父组件中的变量的拷贝。
- @Link:双向同步、修改父子组件中的数据,修改会同步到父子组件中,底层实现的是传递的父子组件中的变量的引用。
Prop单向传递
语法: 将一个显示任务数量和完成情况的卡片组件进行封装
@Component
struct TaskStatic { @Prop finishTask: number // Prop变量不需要初始化@Prop totalTask: number build() { // ===任务数量显示卡片st===// Row() { Text('任务进度:') // 任务进度文字 .whiteFont() .fontSize(30) .fontWeight(FontWeight.Bold) Stack() { // 叠加容器,容器内组件依次入栈,后一个组件覆盖前一个组件 // ===进度条st===// Progress({ value: this.finishTask, total: this.totalTask, type: ProgressType.Ring }) .width(100) // ===进度条end===// Row() { // 两个文本,一个显示完成任务数量,一个显示总的任务数量 Text(this.finishTask.toString()) // 完成任务 .fontSize(24) .fontColor('#76D') Text(`/` + this.totalTask.toString()) // 所有任务 .fontSize(24) .whiteFont() } } } .card() .justifyContent(FlexAlign.SpaceEvenly) // ===任务数量显示卡片end===// }
}
入口代码:
@Entry
@Component
struct PropPage { // 总的任务量 @State totalTask: number = 0 // 完成的任务 @State finishTask: number = 0 build() { Column() { // 标题栏st// Header({ title: '任务统计案例' }) // 任务展示卡片组件 prop单向同步显示// TaskStatic({ finishTask: this.finishTask, totalTask: this.totalTask }) // 任务列表组件//
TaskList({finishTask:$finishTask,totalTask:$totalTask}) } .width('100%') // Column宽度 .height('100%') // Column高度 .backgroundColor('#F1F2F3') }
}
Link变量初始化
语法:
@Component
struct TaskList { @Link finishTask: number // Link变量也不需要初始化@Link totalTask: number //任务数组 @State tasks: Task[] = [] handleTaskChange() { this.totalTask = this.tasks.length // 所有的任务 // 遍历数组,过滤出已经完成的任务数量 this.finishTask = this.tasks.filter(item => item.finished).length } build() { // 子元素中必须只有一个根元素 Column() { // ==新增任务按钮st==// Button('新增任务') .onClick(() => { // 1. 新增任务进入数组 this.tasks.push(new Task()) // 2. 改变任务总数 this.handleTaskChange() }) .width(100) .height(40) .type(ButtonType.Normal) .borderRadius(15) .margin(10) // 新增任按钮end// // 渲染列表st// List({ space: 10 }) { ForEach(this.tasks, (item: Task, index) => { ListItem() { Row() { Text(`新增任务${index}`) .fontSize(24) .whiteFont() Checkbox({ name: 'checkbox' + index.toString() }) .select(item.finished) .selectedColor(0xed6f21) .onChange((value: boolean) => { item.finished = value this.handleTaskChange() }) } .card() .justifyContent(FlexAlign.SpaceBetween) .margin(6) } .swipeAction({ end: this.deleteButton(index) }) // 列表的右滑功能函数 }) } .width('100%') .layoutWeight(1) // 将剩下的空间全部给list元素 .alignListItem(ListItemAlign.Center) // 列表元素居中 // =渲染列表end=// } } @Builder deleteButton(index: number) { // 删除按钮构建函数 Button() { Image($r('app.media.delete')) .width(40) .padding(5) } .width(60) .type(ButtonType.Circle) .backgroundColor(Color.Brown) .onClick(() => { this.tasks.splice(index, 1) // 删除1个 this.handleTaskChange() // 一旦更改任务,就需要重新渲染 }) }
}
TaskList({finishTask:$finishTask,totalTask:$totalTask})}
传值的时候需要传变量的引用:$+变量名
总结
父组件影响子组件用Prop,父子组件相互影响需要用Link,互不影响使用State
其次,允许装饰的变量类型也不一样,Prop只支持string、number、boolean、enum类型,父组件对象类型,子组件是对象属性。不可以是数组、any类型。
而Link,父子类型一致,string、number、boolean、enum、object、class,以及他们的数组。数组中元素增删、替换都会引起刷新。嵌套类型以及数组中的对象属性无法触发视图更新。
初始化方式上看,Prop不允许子组件初始化,Link由父组件传递,禁止子组件初始化。
也提供了另一种传递方式,@Provide 和@Consume,不需要初始化和传值。
import router from '@ohos.router'
import { Header } from '../components/herder'
// 创建Task类
class Task { static id: number = 1 name: string = `任务${Task.id++}` finished: boolean = false
} // 创建单独的卡片样式
@Styles function card() { .width('95%') // .height(20) .padding(20) .backgroundColor(Color.Brown) .borderRadius(15) .shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 })
} // 任务完成样式
@Extend(Text) function finishedCard() { .decoration({ type: TextDecorationType.LineThrough }) .fontColor('#B1B2B1')
} // 任务白色字体
@Extend(Text) function whiteFont() { .fontColor(Color.White)
} @Entry
@Component
struct PropPage { // 总的任务量 @Provide totalTask: number = 0 // 完成的任务 @Provide finishTask: number = 0 build() { Column() { // =======================标题栏st======================// Header({ title: '任务统计案例' }) // =======================任务展示卡片组件======================// TaskStatic() // =======================任务列表组件======================// TaskList() } .width('100%') // Column宽度 .height('100%') // Column高度 .backgroundColor('#F1F2F3') }
} @Component
struct TaskStatic { @Consume finishTask: number @Consume totalTask: number build() { // =======================任务数量显示卡片st======================// Row() { Text('任务进度:') // 任务进度文字 .whiteFont() .fontSize(30) .fontWeight(FontWeight.Bold) Stack() { // 叠加容器,容器内组件依次入栈,后一个组件覆盖前一个组件 // =======================进度条st======================// Progress({ value: this.finishTask, total: this.totalTask, type: ProgressType.Ring }) .width(100) // =======================进度条end======================// Row() { // 两个文本,一个显示完成任务数量,一个显示总的任务数量 Text(this.finishTask.toString()) // 完成任务 .fontSize(24) .fontColor('#76D') Text(`/` + this.totalTask.toString()) // 所有任务 .fontSize(24) .whiteFont() } } } .card() .justifyContent(FlexAlign.SpaceEvenly) // =======================任务数量显示卡片end======================// }
} @Component
struct TaskList { @Consume finishTask: number @Consume totalTask: number //任务数组 @State tasks: Task[] = [] handleTaskChange() { this.totalTask = this.tasks.length // 所有的任务 // 遍历数组,过滤出已经完成的任务数量 this.finishTask = this.tasks.filter(item => item.finished).length } build() { // 子元素中必须只有一个根元素 Column() { // =======================新增任务按钮st======================// Button('新增任务') .onClick(() => { // 1. 新增任务进入数组 this.tasks.push(new Task()) // 2. 改变任务总数 this.handleTaskChange() }) .width(100) .height(40) .type(ButtonType.Normal) .borderRadius(15) .margin(10) // =======================新增任按钮end======================// // =======================渲染列表st======================// List({ space: 10 }) { ForEach(this.tasks, (item: Task, index) => { ListItem() { Row() { Text(`新增任务${index}`) .fontSize(24) .whiteFont() Checkbox({ name: 'checkbox' + index.toString() }) .select(item.finished) .selectedColor(0xed6f21) .onChange((value: boolean) => { item.finished = value this.handleTaskChange() }) } .card() .justifyContent(FlexAlign.SpaceBetween) .margin(6) } .swipeAction({ end: this.deleteButton(index) }) // 列表的右滑功能函数 }) } .width('100%') .layoutWeight(1) // 将剩下的空间全部给list元素 .alignListItem(ListItemAlign.Center) // 列表元素居中 // =======================渲染列表end======================// } } @Builder deleteButton(index: number) { // 删除按钮构建函数 Button() { Image($r('app.media.delete')) .width(40) .padding(5) } .width(60) .type(ButtonType.Circle) .backgroundColor(Color.Brown) .onClick(() => { this.tasks.splice(index, 1) // 删除1个 this.handleTaskChange() // 一旦更改任务,就需要重新渲染 }) }
}