## 一、@Prop概述
在鸿蒙Next中,`@Prop`装饰器用于在父子组件之间建立单向的数据同步关系。这意味着数据从父组件流向子组件,子组件对`@Prop`装饰变量的修改不会同步回父组件。从API version 9开始,该装饰器支持在ArkTS卡片中使用,从API version 11开始,支持在元服务中使用。
### (一)同步机制
1. 父组件状态变量值的修改会同步给子组件`@Prop`装饰的变量。
2. 子组件`@Prop`变量的修改不会影响父组件的状态变量。
3. 当数据源(如父组件中的`@State`变量)更改时,`@Prop`装饰的变量会更新,并且会覆盖本地对该变量的所有更改。
### (二)限制条件
1. `@Prop`装饰变量时会进行深拷贝,除基本类型、Map、Set、Date、Array外,其他类型在拷贝过程中可能丢失类型信息。
2. 不能在`@Entry`装饰的自定义组件中使用`@Prop`装饰器。
## 二、装饰器使用规则
1. **参数**:无参数。
2. **同步类型**:单向同步。
3. **允许装饰的变量类型**
- Object、class、string、number、boolean、enum类型及其数组。
- 支持Date类型。
- API11及以上支持Map、Set类型。
- 支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。
- 必须指定类型,且`@Prop`和数据源类型需相同,有以下三种情况:
- `@Prop`装饰的变量和`@State`以及其他装饰器同步时双方类型必须相同。
- `@Prop`装饰的变量和`@State`以及其他装饰器装饰的数组的项同步时,`@Prop`的类型需要和`@State`装饰的数组的数组项相同。
- 当父组件状态变量为Object或者class时,`@Prop`装饰的变量和父组件状态变量的属性类型相同。
- API11及以上支持上述支持类型的联合类型,如string | number、string | undefined或ClassA | null。
4. **嵌套传递层数**:在组件复用场景,建议`@Prop`深度嵌套数据不要超过5层,以免因深拷贝占用过多空间和导致垃圾回收问题影响性能,此时可考虑使用`@ObjectLink`。
5. **被装饰变量的初始值**:允许本地初始化;若在API 11中和`@Require`结合使用,则必须父组件构造传参。
## 三、变量的传递/访问规则
1. **从父组件初始化**
- 若本地有初始化,则为可选;否则必选。支持父组件中的常规变量(仅初始化数值,变化不触发UI刷新,只有状态变量能触发UI刷新)、`@State`、`@Link`、`@Prop`、`@Provide`、`@Consume`、`@ObjectLink`、`@StorageLink`、`@StorageProp`、`@LocalStorageLink`和`@LocalStorageProp`去初始化子组件中的`@Prop`变量。
2. **用于初始化子组件**:`@Prop`支持去初始化子组件中的常规变量、`@State`、`@Link`、`@Prop`、`@Provide`。
3. **是否支持组件外访问**:`@Prop`装饰的变量是私有的,只能在组件内访问。
## 四、观察变化和行为表现
### (一)观察变化
1. **简单类型**:如`number`、`string`、`boolean`等,可观察到赋值的变化。
2. **复杂类型(Object或class)**
- 可以观察到第一层属性的赋值变化。
- 若class被`@Observed`装饰,可观察到class属性的变化(嵌套场景)。
3. **数组类型**:可观察到数组本身的赋值和数组项的添加、删除和更新。
4. **Date类型**:可观察到Date整体的赋值,以及通过其接口更新属性的操作。
5. **Map类型(API11及以上)**:可观察到Map整体的赋值和通过其接口更新值的操作。
6. **Set类型(API11及以上)**:可观察到Set整体的赋值和通过其接口更新值的操作。
### (二)框架行为
1. **初始渲染**
- 执行父组件的`build()`函数创建子组件实例并传递数据源。
- 初始化子组件`@Prop`装饰的变量。
2. **更新**
- 子组件`@Prop`更新时,仅在当前子组件内,不会同步回父组件。
- 父组件数据源更新时,子组件`@Prop`变量被重置,本地修改被覆盖。
- 注意:`@Prop`装饰的数据更新依赖所属自定义组件的重新渲染,应用进入后台后无法刷新,推荐使用`@Link`代替。
## 五、使用场景示例
### (一)父组件`@State`到子组件`@Prop`简单数据类型同步
父组件`ParentComponent`的`@State`变量`countDownStartValue`初始化子组件`CountDownComponent`的`@Prop`变量`count`。点击父组件按钮修改`countDownStartValue`会同步更新子组件`count`,而子组件修改`count`不会影响父组件。
### (二)父组件`@State`数组项到子组件`@Prop`简单数据类型同步
父组件`Index`的`@State`数组`arr`的数组项初始化子组件`Child`的`@Prop`变量`value`。子组件修改`value`不会同步回父组件,父组件修改`arr`会更新相应子组件的`value`。
### (三)从父组件中的`@State`类对象属性到`@Prop`简单类型的同步
父组件`Library`的`@State`图书对象`book`初始化子组件`ReaderComp`的`@Prop`变量`book`。子组件对`book`的本地更改(如标记为已读)不会同步给父组件。
### (四)从父组件中的`@State`数组项到`@Prop class`类型的同步
父组件`Library`的`@State`数组`allBooks`包含`Book`对象,子组件`ReaderComp`的`@Prop`变量`book`接收数组项。需使用`@Observed`装饰`Book`类,否则无法观察到`Book`对象属性的更改(如标记为已读),且子组件`@Prop`变量的修改不会同步给父组件。
### (五)`@Prop`本地初始化不和父组件同步
子组件`MyComponent`有两个`@Prop`变量,`customCounter`无本地初始化,需父组件提供数据源;`customCounter2`有本地初始化,父组件可选择是否同步数据源,且父组件初始化的值会覆盖子组件本地初始化的值。
### (六)`@Prop`嵌套场景
在嵌套场景下,每一层类都要用`@Observed`装饰,且每一层都要被`@Prop`接收,才能观察到嵌套场景中的数据变化。例如,父组件`Parent`的`@State`变量`votes`(类型为`ClassB`,包含`ClassA`对象),通过`@Prop`传递给子组件`Child1`,并在多层嵌套结构中实现数据的单向同步和观察。
## 六、常见问题
1. **`@Prop`装饰状态变量未初始化错误**:确保`@Prop`装饰的变量在合适的时机进行初始化。
2. **使用`a.b(this.object)`形式调用,不会触发UI刷新**:注意只有状态变量的变化才能触发UI刷新,常规变量赋值给`@Prop`仅初始化数值,其变化不触发UI刷新。