【鸿蒙学习笔记】@Prop装饰器:父子单向同步

官方文档:@Prop装饰器:父子单向同步

[Q&A] @Prop装饰器作用

@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。

[Q&A] @Prop装饰器特点

1・@Prop装饰器不能在@Entry装饰的自定义组件中使用。
2・@Prop装饰变量时会进行深拷贝,在拷贝的过程中除了基本类型、Map、Set、Date、Array外,都会丢失类型。

样例1:Date

@Component
struct DateComponent {
  @Prop childSelectedDate: Date = new Date('');

  build() {
    Column() {
      DatePicker({
        start: new Date('1970-1-1'),
        end: new Date('2100-1-1'),
        selected: this.childSelectedDate
      })

      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')
        })
    }
  }
}

@Entry
@Component
struct PracExample {
  @State parentSelectedDate: Date = new Date('2008-08-08');

  build() {
    Column() {
      DatePicker({
        start: new Date('1970-1-1'),
        end: new Date('2100-1-1'),
        selected: this.parentSelectedDate
      })

      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')
        })

      DateComponent({ childSelectedDate: this.parentSelectedDate })
    }
  }
}

在这里插入图片描述

样例2:父组件@State简单数据类型→子组件@Prop简单数据类型同步

@Component
struct AComponent {
  @Prop sonCount: number = 0;
  step: number = 1;

  build() {
    Row() {
      if (this.sonCount > 0) {
        Text(`当前值为 ${this.sonCount} `).fontSize(20)
      } else {
        Text('Game over!').fontSize(20)
      }
      Button('子 -step').onClick(() => {
        this.sonCount -= this.step;
      })
    }
  }
}

@Entry
@Component
struct PracExample {
  @State count: number = 10;

  build() {
    Column() {
      Row() {
        if (this.count > 0) {
          Text(`当前值为 ${this.count}`).fontSize(20)
        } else {
          Text('Game over!').fontSize(20)
        }

        Button('父 +1 ').margin(5).width("20%")
          .onClick(() => {
            this.count += 1;
          })

        Button('父 -1 ').margin(5).width("20%")
          .onClick(() => {
            this.count -= 1;
          })
      }

      AComponent({ sonCount: this.count, step: 2 })
    }
  }
}

在这里插入图片描述

样例3:父组件@State数组项→子组件@Prop简单数据类型同步

@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)
        }

        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())
        }

        Divider().height(10)

        Text('重置').fontSize(30).backgroundColor(Color.Pink).width('50%').textAlign(TextAlign.Center).padding(10)
          .onClick(() => {
            this.arr = this.arr[0] == 1 ? [3, 4, 5] : [1, 2, 3];
          })
      }
    }
  }
}

在这里插入图片描述

样例4:父组件@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 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).backgroundColor(Color.Pink)
        .onClick(() => this.book.readIt = true)
    }
  }
}

@Entry
@Component
struct PracExample {
  @State book: Book = new Book('资治通鉴', 765);

  build() {
    Column() {
      ReaderComp({ book: this.book }).margin(10)
      ReaderComp({ book: this.book }).margin(10)
    }
  }
}

在这里插入图片描述

样例5:父组件@State数组项→@Prop class类型的同步

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 PracExample {
  @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')

      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本地初始化不和父组件同步

@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嵌套场景

几个按钮跳的乱,需要消化

@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类型变量

@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(`→`).fontSize(30).margin(10)
          Text(`${item[1]}`).fontSize(30).margin(10)
        }
      })

      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('删除key为0的值').margin(5).width('100%').onClick(() => {
        this.map.delete(0)
      })
    }
  }
}

@Entry
@Component
struct PracExample {
  @State map: Map<number, string> = new Map([[1, "X"], [2, "XX"], [3, "XXX"]])

  build() {
    Column() {
      MapComponent({ map: this.map })
    }.width('100%').backgroundColor(Color.Green)
  }
}

在这里插入图片描述

样例9:装饰Set类型变量

@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支持联合类型实例

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)
  }
}

在这里插入图片描述

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值