管理中心
我的
您当前正在浏览新版开发者文档中心,目录分类和层级有所调整。点击左侧当前文档分类名称前的“☰”图标,可切换文档分类。 了解新版目录

@Prop装饰器:父子单向同步

@Prop装饰的变量可以和父组件建立单向同步关系。

在阅读@Prop文档前,建议开发者首先了解@State的基本用法。最佳实践请参考状态管理最佳实践。常见问题请参考状态管理常见问题。

说明

从API version 9开始,该装饰器支持在ArkTS卡片中使用。

从API version 11开始,该装饰器支持在元服务中使用。

概述

@Prop装饰的变量具有以下特性:

  • @Prop装饰的变量允许本地修改,但修改不会同步回父组件。

  • 当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。

装饰器使用规则说明

展开
@Prop变量装饰器 说明
装饰器参数 无。
同步类型

单向同步。对父组件状态变量值的修改,将同步给子组件@Prop装饰的变量,子组件@Prop装饰的变量的修改不会同步到父组件的状态变量上。

嵌套类型的场景请参考观察变化。

允许装饰的变量类型

Object、class、string、number、boolean、enum类型,以及这些类型的数组。

API version 10开始支持Date类型。

API version 11及以上支持Map、Set类型、undefined和null类型、ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型以及这些类型的联合类型,示例见Prop支持联合类型实例。

支持类型的场景请参考观察变化。

不允许装饰的变量类型 不支持装饰Function类型。
嵌套传递层数 在组件复用场景,建议@Prop深度嵌套数据不要超过5层,嵌套太多会导致深拷贝占用的空间过大以及GarbageCollection(垃圾回收),引起性能问题,此时更建议使用@ObjectLink。
被装饰变量的初始值 允许本地初始化。API version 11及以上,如果和@Require结合使用,则必须父组件构造传参。

变量的传递/访问规则说明

展开
装饰器使用规则 说明
从父组件初始化 如果本地有初始化,则是可选的,初始化行为和@State保持一致。没有的话,则必选,支持父组件中的常规变量(常规变量对@Prop赋值,只是数值的初始化,常规变量的变化不会触发UI刷新。只有状态变量才能触发UI刷新)、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink和@LocalStorageProp去初始化子组件中的@Prop装饰的变量。
用于初始化子组件 @Prop支持初始化子组件中的常规变量、@State、@Link、@Prop、@Provide。
是否支持组件外访问 @Prop装饰的变量是私有的,只能在组件内访问。

初始化规则图示:

观察变化和行为表现

观察变化

@Prop装饰的数据可以观察到以下变化。

  • 当装饰支持类型,可以观察到赋值的变化。简单类型完整示例请参考父组件@State到子组件@Prop简单数据类型同步。

    收起
    自动换行
    深色代码主题
    复制
    1. // 简单类型
    2. @Prop count: number;
    3. // 赋值的变化可以被观察到
    4. this.count = 1;
    5. // 复杂类型
    6. @Prop title: Model;
    7. // 可以观察到赋值的变化
    8. this.title = new Model('Hi');
  • 当装饰的类型是Object或者class复杂类型时,可以观察到自身的赋值和第一层的属性的变化,属性即Object.keys(observedObject)返回的所有属性。复杂类型完整示例请参考从父组件中的@State类对象属性到@Prop简单类型的同步。

    收起
    自动换行
    深色代码主题
    复制
    1. // 定义嵌套类
    2. class Info {
    3. public value: string;
    4. constructor(value: string) {
    5. this.value = value;
    6. }
    7. }
    8. class Model {
    9. public value: string;
    10. public info: Info;
    11. constructor(value: string, info: Info) {
    12. this.value = value;
    13. this.info = info;
    14. }
    15. }
    收起
    自动换行
    深色代码主题
    复制
    1. @Prop title: Model;
    收起
    自动换行
    深色代码主题
    复制
    1. // 可以观察到第一层的变化
    2. this.title.value = 'Hi';
    收起
    自动换行
    深色代码主题
    复制
    1. // 观察不到第二层的变化
    2. this.title.info.value = 'ArkUI';

对于嵌套场景,如果class是被@Observed装饰的,可以观察到class属性的变化,示例请参考@Prop嵌套场景。

  • 当装饰的类型是数组的时候,可以观察到数组本身的赋值和数组项的添加、删除和更新。数组类型完整示例请参考父组件@State数组项到子组件@Prop简单数据类型同步。

    收起
    自动换行
    深色代码主题
    复制
    1. // @Prop装饰的对象为数组时
    2. @Prop title: string[];
    3. // 数组自身的赋值可以观察到
    4. this.title = ['1'];
    5. // 数组项的赋值可以观察到
    6. this.title[0] = '2';
    7. // 删除数组项可以观察到
    8. this.title.pop();
    9. // 新增数组项可以观察到
    10. this.title.push('3');

对于@State和@Prop的同步场景:

  • 使用父组件中@State变量的值初始化子组件中的@Prop装饰的变量。当@State变量变化时,该变量值也会同步更新至@Prop装饰的变量。

  • @Prop装饰的变量的修改不会影响其数据源@State装饰变量的值。

  • 除了@State,数据源也可以用@Link或@Prop装饰,对@Prop的同步机制是相同的。

  • 数据源和@Prop装饰的变量的类型需要相同。

  • 当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性,详见装饰Date类型变量。

  • 当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口set, clear, delete 更新Map的值。详见装饰Map类型变量。

  • 当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口add, clear, delete 更新Set的值。详见装饰Set类型变量。

框架行为

理解@Prop装饰的变量值初始化和更新机制,需要了解父组件和子组件的渲染和更新流程。

  1. 初始渲染:

    1. 执行父组件的build()函数,创建子组件的新实例并传递数据源。
    2. 初始化子组件@Prop装饰的变量。
  2. 更新:

    1. 子组件@Prop更新时,更新仅停留在当前子组件,不会同步回父组件。
    2. 当父组件的数据源更新时,子组件的@Prop装饰的变量将被来自父组件的数据源重置,所有@Prop装饰变量的本地修改将被父组件的更新覆盖。
说明

@Prop同步数据源依赖于数据源所在组件的刷新,而应用进入后台后无法触发刷新,因此应用进入后台后,@Prop无法从数据源更新。在此场景下,若需即时数据同步,推荐使用@Link代替。

以下示例中,当@State装饰的变量message改变时,Father组件会刷新。由于Son组件使用@Prop接收了该变量,因此Father组件刷新的过程中会使用message的最新值去更新@Prop的值。@Prop更新后,会触发Son组件的刷新。

收起
自动换行
深色代码主题
复制
  1. @Component
  2. struct Son {
  3. @Prop message: string = 'Hi';
  4. build() {
  5. Column() {
  6. Text(this.message)
  7. }
  8. }
  9. }
  10. @Entry
  11. @Component
  12. struct Father {
  13. @State message: string = 'Hello';
  14. build() {
  15. Column() {
  16. Text(this.message)
  17. Button(`father click`).onClick(() => {
  18. this.message += '*';
  19. })
  20. // 父组件@State装饰的message传给子组件的message
  21. Son({ message: this.message })
  22. }
  23. }
  24. }

限制条件

  • @Prop装饰变量时会进行深拷贝,在拷贝的过程中除了基本类型、Map、Set、Date、Array外,都会丢失类型。例如,对于通过NAPI提供的复杂类型(如PixelMap),由于其部分实现在Native侧,因此无法在ArkTS侧通过深拷贝获得完整的数据;同样,RegExp类型在拷贝过程中会丢失原类型,导致被@Prop装饰后无法调用正则相关函数。

  • @Prop不支持装饰Function类型的变量,API version 23之前,应用在运行时会出现错误。

    从API version 23开始,在应用编译时添加了相关校验,@Prop装饰Function类型变量会提示ERROR,应在代码中删除Function类型变量的@Prop装饰器。

  • 父组件传入undefined时,@Prop装饰的变量仍使用本地默认值进行初始化。

    收起
    自动换行
    深色代码主题
    复制
    1. @Entry
    2. @Component
    3. struct Parent {
    4. @State count: number | undefined = undefined;
    5. build() {
    6. Column() {
    7. Text(`Parent count value: ${this.count}`)
    8. .fontSize(20)
    9. .margin(10)
    10. Child({ count: this.count })
    11. }
    12. }
    13. }
    14. @Component
    15. struct Child {
    16. // 父组件传入undefined时,@Prop装饰的变量仍使用本地默认值进行初始化
    17. @Prop count: number | undefined = 0;
    18. build() {
    19. Column() {
    20. Text(`Child count value: ${this.count}`)
    21. .fontSize(20)
    22. .margin(10)
    23. }
    24. }
    25. }

使用场景

父组件@State到子组件@Prop简单数据类型同步

以下示例是@State到子组件@Prop简单数据同步,父组件ParentComponent的状态变量countDownStartValue初始化子组件CountDownComponent中@Prop装饰的count,点击“Try again”,count的修改仅保留在CountDownComponent,不会同步给父组件ParentComponent。

ParentComponent的状态变量countDownStartValue的变化将重置CountDownComponent的count。

收起
自动换行
深色代码主题
复制
  1. @Component
  2. struct CountDownComponent {
  3. @Prop count: number = 0;
  4. costOfOneAttempt: number = 1;
  5. build() {
  6. Column() {
  7. if (this.count > 0) {
  8. Text(`You have ${this.count} Nuggets left`)
  9. } else {
  10. Text('Game over!')
  11. }
  12. // @Prop装饰的变量不会同步给父组件
  13. Button(`Try again`).onClick(() => {
  14. this.count -= this.costOfOneAttempt;
  15. })
  16. }
  17. }
  18. }
  19. @Entry
  20. @Component
  21. struct ParentComponent {
  22. @State countDownStartValue: number = 10;
  23. build() {
  24. Column() {
  25. Text(`Grant ${this.countDownStartValue} nuggets to play.`)
  26. // 父组件的数据源的修改会同步给子组件
  27. Button(`+1 - Nuggets in New Game`).onClick(() => {
  28. this.countDownStartValue += 1;
  29. })
  30. // 父组件的修改会同步给子组件
  31. Button(`-1 - Nuggets in New Game`).onClick(() => {
  32. this.countDownStartValue -= 1;
  33. })
  34. CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
  35. }
  36. }
  37. }

在上面的示例中:

  1. CountDownComponent子组件首次创建时其@Prop装饰的count变量将从父组件@State装饰的countDownStartValue变量初始化。

  2. 按“+1”或“-1”按钮时,父组件的@State装饰的countDownStartValue值会变化,这将触发父组件重新渲染,在父组件重新渲染过程中会刷新使用countDownStartValue状态变量的UI组件,并单向同步更新CountDownComponent子组件中的count值。

  3. 更新count状态变量值也会触发CountDownComponent的重新渲染,在重新渲染过程中,评估使用count状态变量的if语句条件(this.count > 0),并执行true分支中的使用count状态变量的UI组件相关描述来更新Text组件的UI显示。

  4. 当按下子组件CountDownComponent的“Try again”按钮时,其@Prop装饰的变量count将被更改,但是count值的更改不会影响父组件的countDownStartValue值。

  5. 父组件的countDownStartValue值变化时,父组件的修改将覆盖掉子组件CountDownComponent中count本地的修改。

父组件@State数组项到子组件@Prop简单数据类型同步

父组件中@State如果装饰数组类型的变量,其数组项也可以初始化@Prop。以下示例中,父组件Index中@State装饰数组arr,将其数组项初始化子组件Child中@Prop装饰的value。

收起
自动换行
深色代码主题
复制
  1. @Component
  2. struct Child {
  3. @Prop value: number = 0;
  4. build() {
  5. Text(`${this.value}`)
  6. .fontSize(50)
  7. .onClick(() => {
  8. this.value++;
  9. })
  10. }
  11. }
  12. @Entry
  13. @Component
  14. struct Index {
  15. @State arr: number[] = [1, 2, 3];
  16. build() {
  17. Row() {
  18. Column() {
  19. Child({ value: this.arr[0] })
  20. Child({ value: this.arr[1] })
  21. Child({ value: this.arr[2] })
  22. Divider().height(5)
  23. ForEach(this.arr,
  24. (item: number) => {
  25. Child({ value: item })
  26. },
  27. (item: number) => item.toString()
  28. )
  29. Text('replace entire arr')
  30. .fontSize(50)
  31. .onClick(() => {
  32. // 两个数组都包含项“3”。
  33. this.arr = this.arr[0] == 1 ? [3, 4, 5] : [1, 2, 3];
  34. })
  35. }
  36. }
  37. }
  38. }

初始渲染创建6个子组件实例,每个@Prop装饰的变量初始化都在本地拷贝了一份数组项。子组件onClick事件处理程序会更改局部变量值。

如果点击界面上的“1”六次,“2”五次、“3”四次,将所有变量的本地取值都变为“7”。

收起
自动换行
深色代码主题
复制
  1. 7
  2. 7
  3. 7
  4. ——————
  5. 7
  6. 7
  7. 7

点击replace entire arr后,屏幕将显示以下信息。

收起
自动换行
深色代码主题
复制
  1. 3
  2. 4
  3. 5
  4. ——————
  5. 7
  6. 4
  7. 5
  • 在子组件Child中做的所有的修改都不会同步回父组件Index组件,所以即使6个组件显示都为7,但在父组件Index中,this.arr保存的值依旧是[1,2,3]。

  • 点击replace entire arr,this.arr[0] == 1成立,将this.arr赋值为[3, 4, 5]。

  • 因为this.arr[0]已更改,Child({value: this.arr[0]})组件将this.arr[0]更新同步到实例@Prop装饰的变量。Child({value: this.arr[1]})和Child({value: this.arr[2]})的情况也类似。

  • this.arr的更改触发ForEach更新,this.arr更新的前后都有数值为3的数组项:[3, 4, 5] 和[1, 2, 3]。根据diff算法,数组项“3”将被保留,删除“1”和“2”的数组项,添加为“4”和“5”的数组项。这就意味着,数组项“3”的组件不会重新生成,而是将其移动到第一位。所以“3”对应的组件不会更新,此时“3”对应的组件数值为“7”,ForEach最终的渲染结果是“7”,“4”,“5”。

从父组件中的@State类对象属性到@Prop简单类型的同步

如果图书馆有一本图书和两位用户,每位用户都可以将图书标记为已读,此标记行为不会影响其他用户。从代码角度讲,对@Prop图书对象的本地更改不会同步给图书馆组件中的@State图书对象。

在此示例中,图书类可以使用@Observed装饰器,但不是必须的,只有在嵌套结构时需要此装饰器。这一点会在从父组件中的@State数组项到@Prop class类型的同步说明。

收起
自动换行
深色代码主题
复制
  1. class Book {
  2. public title: string;
  3. public pages: number;
  4. public readIt: boolean = false;
  5. constructor(title: string, pages: number) {
  6. this.title = title;
  7. this.pages = pages;
  8. }
  9. }
  10. @Component
  11. struct ReaderComp {
  12. // 父组件@State装饰的book传入子组件@Prop装饰的book
  13. @Prop book: Book = new Book('', 0);
  14. build() {
  15. Row() {
  16. Text(this.book.title)
  17. Text(`...has${this.book.pages} pages!`)
  18. Text(`...${this.book.readIt ? 'I have read' : 'I have not read it'}`)
  19. .onClick(() => this.book.readIt = true)
  20. }
  21. }
  22. }
  23. @Entry
  24. @Component
  25. struct Library {
  26. @State book: Book = new Book('100 secrets of C++', 765);
  27. build() {
  28. Column() {
  29. // 父组件将同一book分别传给两个ReaderComp
  30. ReaderComp({ book: this.book })
  31. ReaderComp({ book: this.book })
  32. }
  33. }
  34. }

从父组件中的@State数组项到@Prop class类型的同步

以下示例中,更改了@State装饰的allBooks数组中Book对象的属性,但点击“Mark read for everyone”时,没有触发UI更新。这是因为该属性是第二层的嵌套属性,@State装饰器只能观察到第一层属性,不会观察到此属性更改,所以框架不会更新ReaderComp。

收起
自动换行
深色代码主题
复制
  1. import { hilog } from '@kit.PerformanceAnalysisKit';
  2. const DOMAIN = 0x0001;
  3. const TAG: string = '[SampleProp]';
  4. let nextId: number = 1;
  5. // @Observed
  6. class Book {
  7. public id: number;
  8. public title: string;
  9. public pages: number;
  10. public readIt: boolean = false;
  11. constructor(title: string, pages: number) {
  12. this.id = nextId++;
  13. this.title = title;
  14. this.pages = pages;
  15. }
  16. }
  17. @Component
  18. struct ReaderComp {
  19. @Prop book: Book = new Book('', 1);
  20. build() {
  21. Row() {
  22. Text(` ${this.book ? this.book.title : 'Book is undefined'}`).fontColor('#e6000000')
  23. Text(` has ${this.book ? this.book.pages : 'Book is undefined'} pages!`).fontColor('#e6000000')
  24. Text(` ${this.book ? this.book.readIt ? 'I have read' : 'I have not read it' : 'Book is undefined'}`)
  25. .fontColor('#e6000000')
  26. .onClick(() => this.book.readIt = true)
  27. }
  28. }
  29. }
  30. @Entry
  31. @Component
  32. struct Library {
  33. @State allBooks: Book[] = [new Book('C#', 765), new Book('JS', 652), new Book('TS', 765)];
  34. build() {
  35. Column() {
  36. Text('library`s all time favorite')
  37. .width(312)
  38. .height(40)
  39. .backgroundColor('#0d000000')
  40. .borderRadius(20)
  41. .margin(12)
  42. .padding({ left: 20 })
  43. .fontColor('#e6000000')
  44. ReaderComp({ book: this.allBooks[2] })
  45. .backgroundColor('#0d000000')
  46. .width(312)
  47. .height(40)
  48. .padding({ left: 20, top: 10 })
  49. .borderRadius(20)
  50. .colorBlend('#e6000000')
  51. Text('Books on loan to a reader')
  52. .width(312)
  53. .height(40)
  54. .backgroundColor('#0d000000')
  55. .borderRadius(20)
  56. .margin(12)
  57. .padding({ left: 20 })
  58. .fontColor('#e6000000')
  59. ForEach(this.allBooks, (book: Book) => {
  60. ReaderComp({ book: book })
  61. .margin(12)
  62. .width(312)
  63. .height(40)
  64. .padding({ left: 20, top: 10 })
  65. .backgroundColor('#0d000000')
  66. .borderRadius(20)
  67. },
  68. (book: Book) => book.id.toString())
  69. Button('Add new')
  70. .width(312)
  71. .height(40)
  72. .margin(12)
  73. .fontColor('#FFFFFF')
  74. .onClick(() => {
  75. this.allBooks.push(new Book('JA', 512));
  76. })
  77. Button('Remove first book')
  78. .width(312)
  79. .height(40)
  80. .margin(12)
  81. .fontColor('#FFFFFF')
  82. .onClick(() => {
  83. if (this.allBooks.length > 0) {
  84. this.allBooks.shift();
  85. } else {
  86. // allBooks为空时输出提示信息
  87. hilog.info(DOMAIN, TAG, 'length <= 0');
  88. }
  89. })
  90. Button('Mark read for everyone')
  91. .width(312)
  92. .height(40)
  93. .margin(12)
  94. .fontColor('#FFFFFF')
  95. .onClick(() => {
  96. this.allBooks.forEach((book) => book.readIt = true)
  97. })
  98. }
  99. }
  100. }

使用@Observed装饰class Book,Book的属性变化将被观察。需要注意的是,@Prop在子组件装饰的状态变量和父组件的数据源是单向同步关系,即ReaderComp中的@Prop book的修改不会同步给父组件Library。而父组件只会在状态变量发生变化的时候,才会触发UI的重新渲染。

收起
自动换行
深色代码主题
复制
  1. @Observed
  2. class Book {
  3. public id: number;
  4. public title: string;
  5. public pages: number;
  6. public readIt: boolean = false;
  7. constructor(title: string, pages: number) {
  8. this.id = nextId++;
  9. this.title = title;
  10. this.pages = pages;
  11. }
  12. }

@Observed装饰的类的实例会被不透明的代理对象包装,此代理可以检测到包装对象内的所有属性更改。如果发生这种情况,此时,代理通知@Prop,@Prop对象值被更新。

@Prop本地初始化不和父组件同步

为了支持@Component装饰的组件复用场景,@Prop支持本地初始化,这样可以让@Prop是否与父组件建立同步关系变得可选。当且仅当@Prop有本地初始化时,从父组件向子组件传递@Prop的数据源才是可选的。

下面的示例中,子组件包含两个@Prop装饰的变量:

  • @Prop customCounter没有本地初始化,所以需要父组件提供数据源去初始化@Prop,并当父组件的数据源变化时,@Prop也将被更新。

  • @Prop customCounter2有本地初始化,在这种情况下,@Prop依旧允许但非强制父组件同步数据源给@Prop。

收起
自动换行
深色代码主题
复制
  1. @Component
  2. struct MyComponent {
  3. @Prop customCounter: number;
  4. @Prop customCounter2: number = 5;
  5. build() {
  6. Column() {
  7. Row() {
  8. Text(`From Main: ${this.customCounter}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 })
  9. }
  10. Row() {
  11. Button('Click to change locally!')
  12. .width(288)
  13. .height(40)
  14. .margin({ left: 30, top: 12 })
  15. .fontColor('#FFFFFF')
  16. .onClick(() => {
  17. this.customCounter2++;
  18. })
  19. }
  20. Row() {
  21. Text(`Custom Local: ${this.customCounter2}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 })
  22. }
  23. }
  24. }
  25. }
  26. @Entry
  27. @Component
  28. struct MainProgram {
  29. @State mainCounter: number = 10;
  30. build() {
  31. Column() {
  32. Row() {
  33. Column() {
  34. // customCounter必须从父组件初始化,因为MyComponent的customCounter成员变量缺少本地初始化;此处,customCounter2可以不做初始化
  35. MyComponent({ customCounter: this.mainCounter })
  36. // customCounter2也可以从父组件初始化,父组件初始化的值会覆盖子组件customCounter2的本地初始化的值
  37. MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
  38. }
  39. }
  40. Row() {
  41. Column() {
  42. Button('Click to change number')
  43. .width(288)
  44. .height(40)
  45. .margin({ left: 30, top: 12 })
  46. .fontColor('#FFFFFF')
  47. .onClick(() => {
  48. this.mainCounter++;
  49. })
  50. }
  51. }
  52. }
  53. }
  54. }

@Prop嵌套场景

在嵌套场景下,每一层都要用@Observed装饰,且每一层都要被@Prop接收,这样才能观察到嵌套场景。

收起
自动换行
深色代码主题
复制
  1. // 以下是嵌套类对象的数据结构。
  2. @Observed
  3. class Son {
  4. public title: string;
  5. constructor(title: string) {
  6. this.title = title;
  7. }
  8. }
  9. @Observed
  10. class Father {
  11. public name: string;
  12. public son: Son;
  13. constructor(name: string, son: Son) {
  14. this.name = name;
  15. this.son = son;
  16. }
  17. }

以下组件层次结构展示了@Prop嵌套场景的数据结构。

收起
自动换行
深色代码主题
复制
  1. @Entry
  2. @Component
  3. struct Person {
  4. @State person: Father = new Father('Hello', new Son('world'));
  5. build() {
  6. Column() {
  7. Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
  8. Button('change Father name')
  9. .width(312)
  10. .height(40)
  11. .margin(12)
  12. .fontColor('#FFFFFF')
  13. .onClick(() => {
  14. this.person.name = 'Hi';
  15. })
  16. Button('change Son title')
  17. .width(312)
  18. .height(40)
  19. .margin(12)
  20. .fontColor('#FFFFFF')
  21. .onClick(() => {
  22. // person被@State装饰,@State无法观测到嵌套类型的变化,直接点击该按钮,此时title已经发生变化,但是无法被观测到。
  23. this.person.son.title = 'ArkUI';
  24. })
  25. Text(this.person.name)
  26. .fontSize(16)
  27. .margin(12)
  28. .width(312)
  29. .height(40)
  30. .backgroundColor('#ededed')
  31. .borderRadius(20)
  32. .textAlign(TextAlign.Center)
  33. .fontColor('#e6000000')
  34. .onClick(() => {
  35. // 点击该按钮,此次变化会被观测到,同时能够观察到Button('change Son title')点击后的效果。
  36. this.person.name = 'Bye';
  37. })
  38. Text(this.person.son.title)
  39. .fontSize(16)
  40. .margin(12)
  41. .width(312)
  42. .height(40)
  43. .backgroundColor('#ededed')
  44. .borderRadius(20)
  45. .textAlign(TextAlign.Center)
  46. .onClick(() => {
  47. this.person.son.title = 'openHarmony';
  48. })
  49. Child({ child: this.person.son })
  50. }
  51. }
  52. }
  53. }
  54. @Component
  55. struct Child {
  56. @Prop child: Son = new Son('');
  57. build() {
  58. Column() {
  59. Text(this.child.title)
  60. .fontSize(16)
  61. .margin(12)
  62. .width(312)
  63. .height(40)
  64. .backgroundColor('#ededed')
  65. .borderRadius(20)
  66. .textAlign(TextAlign.Center)
  67. .onClick(() => {
  68. this.child.title = 'Bye Bye';
  69. })
  70. }
  71. }
  72. }

装饰Array类型变量

在下面的示例中,message类型为number[],点击Button改变message的值,视图会随之刷新。

收起
自动换行
深色代码主题
复制
  1. @Entry
  2. @Component
  3. struct Index {
  4. @State message: number[] = [0, 1, 2, 3];
  5. build() {
  6. Column() {
  7. Child({ message: this.message })
  8. }
  9. }
  10. }
  11. @Component
  12. struct Child {
  13. @Prop message: number[] = [0, 1, 2, 3];
  14. build() {
  15. Row() {
  16. Column() {
  17. ForEach(this.message, (item: number) => {
  18. Text(`${item}`)
  19. .fontSize(20)
  20. .margin(10)
  21. })
  22. // 新增数组元素,触发UI刷新
  23. Button('Push element')
  24. .onClick(() => {
  25. this.message.push(4);
  26. })
  27. .width(300)
  28. .margin(10)
  29. // 删除数组元素,触发UI刷新
  30. Button('Pop element')
  31. .onClick(() => {
  32. this.message.pop();
  33. })
  34. .width(300)
  35. .margin(10)
  36. // 对数组整体重新赋值,触发UI刷新
  37. Button('Reset array')
  38. .onClick(() => {
  39. this.message = [9, 8, 7, 6];
  40. })
  41. .width(300)
  42. .margin(10)
  43. // 更新数组元素,触发UI刷新
  44. Button('Modify element[0]')
  45. .onClick(() => {
  46. this.message[0] = 10;
  47. })
  48. .width(300)
  49. .margin(10)
  50. }
  51. .width('100%')
  52. }
  53. .height('100%')
  54. }
  55. }

装饰Map类型变量

说明

从API version 11开始,@Prop支持Map类型。

在下面的示例中,value类型为Map<number, string>,点击Button改变value的值,视图会随之刷新。

收起
自动换行
深色代码主题
复制
  1. @Component
  2. struct Child {
  3. @Prop value: Map<number, string> = new Map([[0, 'a'], [1, 'b'], [3, 'c']]);
  4. build() {
  5. Column() {
  6. ForEach(Array.from(this.value.entries()), (item: [number, string]) => {
  7. Text(`${item[0]}`).fontSize(30)
  8. Text(`${item[1]}`).fontSize(30)
  9. Divider()
  10. })
  11. // value被@Prop装饰,可以被观察到Map整体的赋值以及调用Map接口带来的变化
  12. Button('child init map').onClick(() => {
  13. this.value = new Map([[0, 'a'], [1, 'b'], [3, 'c']]);
  14. })
  15. Button('child set new one').onClick(() => {
  16. this.value.set(4, 'd');
  17. })
  18. Button('child clear').onClick(() => {
  19. this.value.clear();
  20. })
  21. Button('child replace the first one').onClick(() => {
  22. this.value.set(0, 'aa');
  23. })
  24. Button('child delete the first one').onClick(() => {
  25. this.value.delete(0);
  26. })
  27. }
  28. }
  29. }
  30. @Entry
  31. @Component
  32. struct MapSample {
  33. @State message: Map<number, string> = new Map([[0, 'a'], [1, 'b'], [3, 'c']]);
  34. build() {
  35. Row() {
  36. Column() {
  37. Child({ value: this.message })
  38. }
  39. .width('100%')
  40. }
  41. .height('100%')
  42. }
  43. }

装饰Set类型变量

说明

从API version 11开始,@Prop支持Set类型。

在下面的示例中,message类型为Set<number>,点击Button改变message的值,视图会随之刷新。

收起
自动换行
深色代码主题
复制
  1. @Component
  2. struct Child {
  3. @Prop message: Set<number> = new Set([0, 1, 2, 3, 4]);
  4. build() {
  5. Column() {
  6. ForEach(Array.from(this.message.entries()), (item: [number, number]) => {
  7. Text(`${item[0]}`).fontSize(30)
  8. Divider()
  9. })
  10. // message被@Prop装饰,可以被观察到Set整体的赋值以及调用Set接口带来的变化
  11. Button('init set').onClick(() => {
  12. this.message = new Set([0, 1, 2, 3, 4]);
  13. })
  14. Button('set new one').onClick(() => {
  15. this.message.add(5);
  16. })
  17. Button('clear').onClick(() => {
  18. this.message.clear();
  19. })
  20. Button('delete the first one').onClick(() => {
  21. this.message.delete(0);
  22. })
  23. }
  24. .width('100%')
  25. }
  26. }
  27. @Entry
  28. @Component
  29. struct SetSample {
  30. @State message: Set<number> = new Set([0, 1, 2, 3, 4]);
  31. build() {
  32. Row() {
  33. Column() {
  34. Child({ message: this.message })
  35. }
  36. .width('100%')
  37. }
  38. .height('100%')
  39. }
  40. }

装饰Date类型变量

在下面的示例中,selectedDate类型为Date,点击Button改变Date的值,视图会随之刷新。

收起
自动换行
深色代码主题
复制
  1. @Component
  2. struct DateComponent {
  3. @Prop selectedDate: Date = new Date('');
  4. build() {
  5. Column() {
  6. // selectedDate被@Prop装饰,可以被观察到Date整体的赋值以及调用Date接口带来的变化
  7. Button('child update the new date')
  8. .margin(10)
  9. .onClick(() => {
  10. this.selectedDate = new Date('2023-09-09');
  11. })
  12. Button(`child increase the year by 1`).onClick(() => {
  13. this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1);
  14. })
  15. DatePicker({
  16. start: new Date('1970-1-1'),
  17. end: new Date('2100-1-1'),
  18. selected: this.selectedDate
  19. })
  20. }
  21. }
  22. }
  23. @Entry
  24. @Component
  25. struct ParentComponent {
  26. @State parentSelectedDate: Date = new Date('2021-08-08');
  27. build() {
  28. Column() {
  29. Button('parent update the new date')
  30. .margin(10)
  31. .onClick(() => {
  32. this.parentSelectedDate = new Date('2023-07-07');
  33. })
  34. Button('parent increase the day by 1')
  35. .margin(10)
  36. .onClick(() => {
  37. this.parentSelectedDate.setDate(this.parentSelectedDate.getDate() + 1);
  38. })
  39. DatePicker({
  40. start: new Date('1970-1-1'),
  41. end: new Date('2100-1-1'),
  42. selected: this.parentSelectedDate
  43. })
  44. DateComponent({ selectedDate: this.parentSelectedDate })
  45. }
  46. }
  47. }

Prop支持联合类型实例

@Prop支持联合类型和undefined和null,在下面的示例中,animal类型为Animals | undefined,点击父组件Zoo中的Button改变animal的属性或者类型,Child中也会对应刷新。

收起
自动换行
深色代码主题
复制
  1. import { hilog } from '@kit.PerformanceAnalysisKit';
  2. const DOMAIN = 0x0001;
  3. const TAG: string = '[SampleProp]';
  4. class Animals {
  5. public name: string;
  6. constructor(name: string) {
  7. this.name = name;
  8. }
  9. }
  10. @Component
  11. struct Child {
  12. @Prop animal: Animals | undefined;
  13. build() {
  14. Column() {
  15. Text(`Child's animal is ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30)
  16. Button('Child change animals into tigers')
  17. .onClick(() => {
  18. // 赋值为Animals的实例
  19. this.animal = new Animals('Tiger');
  20. })
  21. Button('Child change animal to undefined')
  22. .onClick(() => {
  23. // 赋值为undefined
  24. this.animal = undefined;
  25. })
  26. }.width('100%')
  27. }
  28. }
  29. @Entry
  30. @Component
  31. struct Zoo {
  32. @State animal: Animals | undefined = new Animals('lion');
  33. build() {
  34. Column() {
  35. Text(`Parents' animals are ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30)
  36. Child({ animal: this.animal })
  37. Button('Parents change animals into dogs')
  38. .onClick(() => {
  39. // 判断animal的类型,做属性的更新
  40. if (this.animal instanceof Animals) {
  41. this.animal.name = 'Dog';
  42. } else {
  43. hilog.info(DOMAIN, TAG, 'num is undefined, cannot change property');
  44. }
  45. })
  46. Button('Parents change animal to undefined')
  47. .onClick(() => {
  48. // 赋值为undefined
  49. this.animal = undefined;
  50. })
  51. }
  52. }
  53. }