文章大纲
引言
前一篇文章鸿蒙 入门——ArkUI 自定义组件内同步的装饰器@State小结(二)简单组件内的装饰器@State的用法,@State修饰的作为数据源可以与多种组件间的装饰器组合使用从而实现组件间的数据同步,这一篇就是介绍下父——>子的单向同步@Prop。
一、@Prop 概述
@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。
@Prop变量允许在本地修改,但修改后的变化不会同步回父组件。
当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。因此,数值的同步是父组件到子组件(所属组件),子组件数值的变化不会同步到父组件。
1、@Prop 限制条件
-
@Prop装饰变量时会进行深拷贝,在拷贝的过程中除了基本类型、Map、Set、Date、Array外,都会丢失类型。例如PixelMap等通过NAPI提供的复杂类型,由于有部分实现在Native侧,因此无法在ArkTS侧通过深拷贝获得完整的数据。
-
@Prop装饰器不能在@Entry装饰的自定义组件中使用。
2、@Prop使用规则
3、变量传递/访问规则
4、@Prop变量值初始化和更新机制
父组件和拥有@Prop变量的子组件初始渲染和更新流程如下:
4.1、初始渲染:
- 执行父组件的build()函数将创建子组件的新实例,将数据源传递给子组件;
初始化子组件@Prop装饰的变量。
4.2、更新:
子组件@Prop更新时,更新仅停留在当前子组件,不会同步回父组件;
当父组件的数据源更新时,子组件的@Prop装饰的变量将被来自父组件的数据源重置,所有@Prop装饰的本地的修改将被父组件的更新覆盖。
@Prop装饰的数据更新依赖其所属自定义组件的重新渲染,所以在应用进入后台后,@Prop无法刷新,推荐使用@Link代替。
二、@Prop 父向子单向同步
即更新父组件的@State值影响到子组件中的@Prop变量,把父组件的@State的变量与子组件的@Prop 绑定起来,当父组件的数据源发生改变时,子组件的相应发生同步改变。使用父组件中@State变量的值初始化子组件中的@Prop变量。当@State变量变化时,该变量值也会同步更新至@Prop变量。但@Prop装饰的变量的修改不会影响其数据源@State装饰变量的值。
1、@Prop 修饰基础类型
@Component
struct CountDownComponent {
@Prop count: number;
cost : number = 1;
build() {
Column() {
if(this.count > 0){
Text(`You have ${this.count} Nuggets left`)
}else {
Text('Game over!')
}
// @Prop装饰的变量不会同步给父组件
Button(`Try again`).onClick(() => {
this.count -= this.cost;
})
}
}
}
@Entry
@Component
struct ParentComponent {
@State countDownStartValue: number = 10;
build() {
Column() {
Text(`Grant ${this.countDownStartValue} nuggets to play.`)
// 父组件的数据源的修改会同步给子组件
Button(`+1 - Nuggets in New Game`).onClick(() => {
this.countDownStartValue += 1;
})
// 父组件的修改会同步给子组件
Button(`-1 - Nuggets in New Game`).onClick(() => {
this.countDownStartValue -= 1;
})
CountDownComponent({ count: this.countDownStartValue, cost: 5 })
}
}
}
2、@Prop 修饰数组项
父组件@State数组项到子组件@Prop简单数据类型同步,即把父组件的@State数组项与子组件的@Prop 绑定起来,当父组件的数据源发生改变时,子组件的相应发生同步改变。
@Component
struct Child {
@Prop value: number;
build() {
Text(`${this.value}`)
.fontSize(50)
.onClick(()=>{this.value++})
}
}
@Entry
@Component
struct Index {
@State arr: number[] = [1,2,3];
build() {
Row() {
Column() {
Child({value: this.arr[0]})
Child({value: this.arr[1]})
Child({value: this.arr[2]})
Divider().height(5)
ForEach(this.arr,
(item:number) => {
Child({value: item})
},
(item:number) => item.toString()
)
Text('replace entire arr')
.fontSize(50)
.onClick(()=>{
this.arr = this.arr[0] == 1 ? [3,4,5] : [1,2,3];
})
}
}
}
}
3、从父组件中的@State类对象属性到@Prop简单类型的同步
把父组件的@State类对象属性与子组件的@Prop 绑定起来,当父组件的数据源发生改变时,子组件的相应发生同步改变。
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 title: string;
@Prop readIt: boolean = false;
build() {
Row() {
Text(this.title)
Text(`... ${this.readIt ? 'I have read' : 'I have not read it'}`)
.onClick(() => this.readIt = true)
}
}
}
@Entry
@Component
struct Library {
@State book: Book = new Book('100 secrets of C++', 765);
build() {
Column() {
ReaderComp({ title: this.book.title, readIt: this.book.readIt })
ReaderComp({ title: this.book.title, readIt: this.book.readIt })
}
}
}
4、在子组件中定义的@Prop成员变量可以做本地初始化,也可以从父组件初始化,在父组件的初始化会把本地初始化覆盖掉
@Component
struct MyComponent {
@Prop customCounter: number;
@Prop customCounter2: number = 5;
build() {
Column() {
Row() {
Text(`From Main: ${this.customCounter}`).width(90).height(40).fontColor('#FF0010')
}
Row() {
Button('Click to change locally !').width(180).height(60).margin({ top: 10 })
.onClick(() => {
this.customCounter2++
})
}.height(100).width(180)
Row() {
Text(`Custom Local: ${this.customCounter2} && ${this.customCounter}`).width(120).height(40).fontColor('#FF0010')
}
}
}
}
@Entry
@Component
struct MainProgram {
@State mainCounter: number = 10;
build() {
Column() {
Row() {
Column() {
Button('Click to change From Parent').width(480).height(60).margin({ top: 10, bottom: 10 })
.onClick(() => {
this.mainCounter++
})
}
}
Row() {
Column()
// customCounter必须从父组件初始化,因为MyComponent的customCounter成员变量缺少本地初始化;此处,customCounter2可以不做初始化。
MyComponent({ customCounter: this.mainCounter })
// customCounter2也可以从父组件初始化,父组件初始化的值会覆盖子组件customCounter2的本地初始化的值
MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
}
}
}
}