官方文档:@Prop装饰器:父子单向同步
[Q&A] @Prop装饰器作用
@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。
[Q&A] @Prop装饰器特点
1・@Prop装饰器不能在@Entry装饰的自定义组件中使用。
2・@Prop装饰变量时会进行深拷贝,在拷贝的过程中除了基本类型、Map、Set、Date、Array外,都会丢失类型。
样例1:Date
cpp
@Component
struct DateComponent {
@Prop childSelectedDate: Date = new Date('');
build() {
Column() {
Row() {
Button('child +1年').margin(5).width('30%')
.onClick(() => {
this.childSelectedDate.setFullYear(this.childSelectedDate.getFullYear() + 1)
})
Button('child +1月').margin(5).width('30%')
.onClick(() => {
this.childSelectedDate.setMonth(this.childSelectedDate.getMonth() + 1)
})
Button('child +1天').margin(5).width('30%')
.onClick(() => {
this.childSelectedDate.setDate(this.childSelectedDate.getDate() + 1)
})
}.width('100%')
Button('child 重置日期').margin(10).width('100%')
.onClick(() => {
this.childSelectedDate = new Date('2023-07-07')
})
DatePicker({
start: new Date('1970-1-1'),
end: new Date('2100-1-1'),
selected: this.childSelectedDate
})
}
}
}
@Entry
@Component
struct SizeExample {
@State parentSelectedDate: Date = new Date('2008-08-08');
build() {
Column() {
Row() {
Button('parent +1年').margin(5).width('30%')
.onClick(() => {
this.parentSelectedDate.setFullYear(this.parentSelectedDate.getFullYear() + 1)
})
Button('parent +1月').margin(5).width('30%')
.onClick(() => {
this.parentSelectedDate.setMonth(this.parentSelectedDate.getMonth() + 1)
})
Button('parent +1天').margin(5).width('30%')
.onClick(() => {
this.parentSelectedDate.setDate(this.parentSelectedDate.getDate() + 1)
})
}.width('100%')
Button('parent 重置日期').margin(10).width('100%')
.onClick(() => {
this.parentSelectedDate = new Date('2023-07-07')
})
DatePicker({
start: new Date('1970-1-1'),
end: new Date('2100-1-1'),
selected: this.parentSelectedDate
})
DateComponent({ childSelectedDate: this.parentSelectedDate })
}
}
}
样例2:父组件@State简单数据类型→子组件@Prop简单数据类型同步
cpp
@Component
struct CountDownComponent {
@Prop count: number = 0;
costOfOneAttempt: number = 1;
build() {
Row() {
if (this.count > 0) {
Text(`当前值为 ${this.count} .`).fontSize(20).margin(10).textAlign(TextAlign.Center)
} else {
Text('Game over!').fontSize(20).margin(10)
}
Button('子 -1').onClick(() => {
this.count -= this.costOfOneAttempt;
}).width("40%")
}.width('100%')
}
}
@Entry
@Component
struct SizeExample {
@State countDownStartValue: number = 10;
build() {
Column() {
Row() {
if (this.countDownStartValue > 0) {
Text(`当前值为 ${this.countDownStartValue} .`).fontSize(20).margin(5).textAlign(TextAlign.Center)
} else {
Text('Game over!').fontSize(20).margin(10)
}
Button('父 +1 ').onClick(() => {
this.countDownStartValue += 1;
}).margin(5).width("20%")
Button('父 -1 ').onClick(() => {
this.countDownStartValue -= 1;
}).margin(5).width("20%")
}.width('100%')
CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
}
}
}
样例3:父组件@State数组项→子组件@Prop简单数据类型同步
cpp
@Component
struct Child {
@Prop value: number = 0;
build() {
Text(`${this.value}`).fontSize(30)
.onClick(() => {
this.value++
})
}
}
@Entry
@Component
struct SizeExample {
@State arr: number[] = [1, 2, 3];
build() {
Row() {
Column() {
Text(`当前值为 ${this.arr[0]},${this.arr[1]},${this.arr[2]}.`).fontSize(30).margin(10).textAlign(TextAlign.Center)
Divider().height(10)
Row() {
Child({ value: this.arr[0] }).width('30%').align(Alignment.Center).backgroundColor(Color.Red)
Child({ value: this.arr[1] }).width('30%').align(Alignment.Center).backgroundColor(Color.Green)
Child({ value: this.arr[2] }).width('30%').align(Alignment.Center).backgroundColor(Color.Yellow)
}.alignItems(VerticalAlign.Center)
Divider().height(10)
Row() {
ForEach(this.arr, (item: number) => {
Child({ value: item }).width('30%').align(Alignment.Center).backgroundColor(Color.Orange).border({ width: 1, style: BorderStyle.Dashed })
}, (item: string) => item.toString())
}.alignItems(VerticalAlign.Center)
Divider().height(10)
Text('重置').fontSize(30).backgroundColor(Color.Pink).width('50%').textAlign(TextAlign.Center).padding(10)
.onClick(() => {
// 两个数组都包含项"3"。
this.arr = this.arr[0] == 1 ? [3, 4, 5] : [1, 2, 3];
})
}
}
}
}
样例4:父组件@State类对象属性→@Prop简单类型的同步
cpp
class Book {
public title: string;
public pages: number;
public readIt: boolean = false;
constructor(title: string, pages: number) {
this.title = title;
this.pages = pages;
}
}
@Component
struct ReaderComp {
@Prop book: Book = new Book("", 0);
build() {
Row() {
Text(this.book.title).fontSize(20)
Text(` 有 ${this.book.pages} 页! `).fontSize(20)
Text(`${this.book.readIt ? " 我读了" : ' 我还没开始读'}`).fontSize(20)
.onClick(() => this.book.readIt = true).backgroundColor(Color.Pink)
}
}
}
@Entry
@Component
struct SizeExample {
@State book: Book = new Book('资治通鉴', 765);
build() {
Column() {
ReaderComp({ book: this.book })
ReaderComp({ book: this.book })
}
}
}
样例5:父组件@State数组项→@Prop class类型的同步
cpp
let nextId: number = 1;
@Observed // @Prop在子组件装饰的状态变量和父组件的数据源是单向同步关系,需要使用@Observed装饰class Book,Book的属性将被观察。
class Book {
public id: number;
public title: string;
public pages: number;
public readIt: boolean = false;
constructor(title: string, pages: number) {
this.id = nextId++;
this.title = title;
this.pages = pages;
}
}
@Component
struct ReaderComp {
@Prop book: Book = new Book("", 1);
build() {
Row() {
Text(` ${this.book ? this.book.title : "未定义"}`).fontColor(Color.Red)
Text(` 有 ${this.book ? this.book.pages : "未定义"} 页!`).fontColor(Color.Black)
Text(` ${this.book ? (this.book.readIt ? "我已经读了" : '我还没读' ): "未定义"}`).fontColor(Color.Blue)
.onClick(() => this.book.readIt = true)
}
}
}
@Entry
@Component
struct SizeExample {
@State allBooks: Book[] = [new Book("资治通鉴", 100), new Book("史记", 100), new Book("论语", 100)];
build() {
Column() {
Text('畅销书').width(312).height(40).fontSize(20).margin(12).fontColor('#e6000000')
ReaderComp({ book: this.allBooks[2] }).backgroundColor('#0d000000').width(312).height(40).padding({ left: 20, top: 10 }).borderRadius(20).colorBlend('#e6000000')
Divider()
Text('已经购买的书').width(312).height(40).fontSize(20).margin(12).fontColor('#e6000000')
ForEach(this.allBooks, (book: Book) => {
ReaderComp({ book: book }).margin(12).width(312).height(40).padding({ left: 20, top: 10 }).backgroundColor('#0d000000').borderRadius(20)
},(book: Book) => book.id.toString())
Button('追加').width(312).height(40).margin(12).fontColor('#FFFFFF 90%')
.onClick(() => {
this.allBooks.push(new Book("孟子", 100));
})
Button('移除第一本书').width(312).height(40).margin(12).fontColor('#FFFFFF 90%')
.onClick(() => {
if (this.allBooks.length > 0){
this.allBooks.shift();
} else {
console.log("length <= 0")
}
})
Button("所有人都已经读了").width(312).height(40).margin(12).fontColor('#FFFFFF 90%')
.onClick(() => {
this.allBooks.forEach((book) => book.readIt = true)
})
}
}
}
样例6:@Prop本地初始化不和父组件同步
cpp
@Component
struct MyComponent {
@Prop count1: number;
@Prop count2: number = 5;
build() {
Column() {
Row() {
Text(`Parent: ${this.count1}`).fontColor('#ff6b6565').width('30%')
Text(`Child: ${this.count2}`).fontColor('#ff6b6565').width('30%')
Button('改变Child数值').width('30%').height(40).fontColor('#FFFFFF,90%')
.onClick(() => {
this.count2++
})
}
}
}
}
@Entry
@Component
struct SizeExample {
@State mainCount: number = 10;
build() {
Column() {
Row() {
Column() {
MyComponent({ count1: this.mainCount })
Divider().height(10)
MyComponent({ count1: this.mainCount, count2: this.mainCount })
}
}
Divider().height(10)
Column() {
Button('改变Parent数值').width(288).height(40).width('100%').fontColor('#FFFFFF,90%')
.onClick(() => {
this.mainCount++
})
}
}
}
}
样例7:@Prop嵌套场景
几个按钮跳的乱,需要消化
cpp
@Entry
@Component
struct SizeExample {
@State book: Book = new Book('Book名字', new Title('Book标题'))
build() {
Column() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
Text(this.book.name).fontSize(16).margin(12).width(312).height(40).backgroundColor(Color.Green).borderRadius(20).textAlign(TextAlign.Center).fontColor('#e6000000')
.onClick(() => {
this.book.name = '资治通鉴'
})
Text(this.book.title.title).fontSize(16).margin(12).width(312).height(40).backgroundColor(Color.Green).borderRadius(20).textAlign(TextAlign.Center)
.onClick(() => {
this.book.title.title = "资治通鉴 标题"
})
ShowTitle({ title: this.book.title })
Button('修改 Book 名字').width(312).height(40).margin(12).fontColor('#FFFFFF,90%')
.onClick(() => {
this.book.name = "史记"
})
Button('修改 Book 标题').width(312).height(40).margin(12).fontColor('#FFFFFF,90%')
.onClick(() => {
this.book.title.title = "史记 标题"
})
}
}
}
}
@Component
struct ShowTitle {
@Prop title: Title = new Title('');
build() {
Column() {
Text(this.title.title).fontSize(16).margin(12).width(312).height(40).backgroundColor(Color.Orange).borderRadius(20).textAlign(TextAlign.Center)
.onClick(() => {
this.title.title = '点击完毕'
})
}
}
}
// 以下是嵌套类对象的数据结构。
@Observed
class Title {
public title: string;
constructor(title: string) {
this.title = title;
}
}
@Observed
class Book {
public name: string;
public title: Title;
constructor(name: string, cla: Title) {
this.name = name;
this.title = cla;
}
}
样例8:装饰Map类型变量
cpp
@Component
struct MapComponent {
@Prop map: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
build() {
Column() {
ForEach(Array.from(this.map.entries()), (item: [number, string]) => {
Row(){
Text(`${item[0]}`).fontSize(30).margin(10)
Text(`${item[1]}`).fontSize(30).margin(10)
}
Divider()
})
Button('初始化').margin(5).width('100%').onClick(() => {
this.map = new Map([[0, "a"], [1, "b"], [3, "c"]])
})
Button('追加1组值').margin(5).width('100%').onClick(() => {
this.map.set(4, "d")
})
Button('清除所有值').margin(5).width('100%').onClick(() => {
this.map.clear()
})
Button('替换第1个值').margin(5).width('100%').onClick(() => {
this.map.set(0, "aa")
})
Button('删除第1个值').margin(5).width('100%').onClick(() => {
this.map.delete(0)
})
}
}
}
@Entry
@Component
struct SizeExample {
@State message: Map<number, string> = new Map([[1, "a"], [2, "b"], [3, "c"]])
build() {
Row() {
Column() {
MapComponent({ map: this.message })
}.width('100%').backgroundColor(Color.Green)
}.height('100%').backgroundColor(Color.Pink)
}
}
样例9:装饰Set类型变量
cpp
@Component
struct Child {
@Prop message: Set<number> = new Set([0, 1, 2, 3, 4])
build() {
Column() {
Row() {
ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
Text(`${item[0]}`).fontSize(30).margin(10)
})
}
Button('初始化Set').width('100%').onClick(() => {
this.message = new Set([0, 1, 2, 3, 4])
})
Divider().height(10)
Button('追加新值').width('100%').onClick(() => {
this.message.add(5)
})
Divider().height(10)
Button('清除所有值').width('100%').onClick(() => {
this.message.clear()
})
Divider().height(10)
Button('删除第一个值').width('100%').onClick(() => {
this.message.delete(0)
})
}.width('100%')
}
}
@Entry
@Component
struct SizeExample {
@State message: Set<number> = new Set([0, 1, 2, 3, 4])
build() {
Row() {
Column() {
Child({ message: this.message })
}.width('100%')
}.height('100%')
}
}
样例10:Prop支持联合类型实例
cpp
class Animals {
public name: string;
constructor(name: string) {
this.name = name;
}
}
@Component
struct Child {
@Prop animal: Animals | undefined;
build() {
Column() {
Text(`子 ${this.animal instanceof Animals ? this.animal.name : '未定义'}`).fontSize(20).backgroundColor(Color.Pink).width('100%').padding(5)
Button('子 改为猫').width('100%').margin(5)
.onClick(() => {
// 赋值为Animals的实例
this.animal = new Animals("猫")
})
Button('子 改为 undefined').width('100%').margin(5)
.onClick(() => {
// 赋值为undefined
this.animal = undefined
})
}.width('100%')
}
}
@Entry
@Component
struct SizeExample {
@State animal: Animals | undefined = new Animals("老虎");
build() {
Column() {
Text(`父 ${this.animal instanceof Animals ? this.animal.name : '未定义'}`).fontSize(20).textAlign(TextAlign.Start).backgroundColor(Color.Yellow).width('100%').padding(5)
Child({ animal: this.animal })
Button('父 改为狗').width('100%').margin(5)
.onClick(() => {
if (this.animal instanceof Animals) {
this.animal.name = "狗"
} else {
console.info('num is undefined, cannot change property')
}
})
Button('父 改为 undefined').width('100%').margin(5)
.onClick(() => {
this.animal = undefined
})
}.backgroundColor(Color.Orange)
}
}