当前位置:   article > 正文

鸿蒙系列--装饰器_鸿蒙装饰器

鸿蒙装饰器

一、基础UI组件结构

        每个UI组件需要定义为@Component struct对象,其内部必须包含一个且只能包含一个build(){}函数,用于绘制UI;struct之内、build()函数之外的地方用于存放数据。

@State:组件拥有的状态属性,当@State 装饰的变量更改时,组件会重新渲染更新 UI。

@Link:组件依赖于其父组件拥有的某些状态属性,当任何一个组件中的数据更新时,另一个组件的状态都会更新,父子组件重新渲染。

@Prop:类似@Link,但子组件所做的更改不会同步到父组件上,属于单向传递。

@Provide:作为数据的提供方,可以更新其子节点的数据,并触发页面渲染

@Consume:在感知到@Provide 数据更新后,会触发当前自定义组件的重新渲染。

@StorageLink :组件通过使用@StorageLink(key)装饰的状态变量,与AppStorage 建立双向数据绑定,key为 AppStorage 中的属性键值。当创建包含@StorageLink 的状态变量的组件时,该状态变量的值将使用AppStorage 中的值进行初始化。在 UI 组件中对@StorageLink 的状态变量所做的更改将同步到AppStorage ,并从 AppStorage 同步到任何其他绑定实例中。

@StorageProp :组件通过使用@StorageProp(key)装饰的状态变量,将与 AppStorage 建立单向数据绑定,key 标识 AppStorage 中的属性键值。当创建包含@StoageProp 的状态变量的组件时,该状态变量的值将使用 AppStorage 中的值进行初始化。AppStorage 中的属性值的更改会导致绑定的 UI 组件进行状态更新。

二、基本UI装饰器

@Entry

装饰struct,页面的入口

@Component

装饰struct,表示该struct具有基于组件的能力

  1. @Entry
  2. @Component
  3. struct TestPage {
  4. build() {
  5. ……
  6. }
  7. }

三、数据装饰器

@State 父子相互独立

  • 装饰的变量是组件的局部变量,必须本地初始化,可通过构造参数赋值
  • 当该数据被修改时,所在组件的build()方法会被重新调用,会重新绘制所在UI
子组价:
  1. @Component
  2. export struct ComponentPage {
  3. @State count: number = 0
  4. private toggleClick() {
  5. this.count += 1
  6. }
  7. build() {
  8. Row() {
  9. Column({ space: 20 }) {
  10. Button(`这是子组件,${this.count}`)
  11. .fontSize(24)
  12. .onClick(this.toggleClick.bind(this))
  13. }
  14. .width('100%')
  15. }
  16. }
  17. }
父组件:
  1. import { ComponentPage } from "./ComponentPage"
  2. @Entry
  3. @Component
  4. struct StatePage {
  5. @State count: number = 0
  6. private toggleClick() {
  7. this.count += 1
  8. }
  9. build() {
  10. Row() {
  11. Column({ space: 20 }) {
  12. Button(`这是父组件,当前值: ${this.count}`)
  13. .fontSize(24)
  14. .onClick(this.toggleClick.bind(this))
  15. //使用默认初始化值
  16. ComponentPage()
  17. //设置count初始值为:20
  18. ComponentPage({ count: 20 })
  19. }
  20. .width('100%')
  21. }.height('100%')
  22. }
  23. }
描述:
  • 当被点击之后修改了count的值,页面会重新绘制UI
  • 子组件中的count和父组件的count互不影响
  • 可以给子组件构造方法设置初始值
  • 使用@State修饰的变量必须初始化
效果图:

@Prop 父子单向同步

  • 继承@State的所有功能
  • 被其装饰的变量可以和父组件建立单向同步关系。@Prop装饰的变量是可变的,但修改不会同步回父组件,当父组件的@State变化时,本地修改的@Prop会被覆盖
子组件:
  1. @Component
  2. export struct ComponentPage {
  3. @Prop count: number
  4. private toggleClick() {
  5. this.count += 1
  6. }
  7. build() {
  8. Row() {
  9. Column({ space: 20 }) {
  10. Button(`这是子组件,${this.count}`)
  11. .fontSize(24)
  12. .onClick(this.toggleClick.bind(this))
  13. }
  14. .width('100%')
  15. }
  16. }
  17. }
父组件:
  1. import { ComponentPage } from "./ComponentPage"
  2. @Entry
  3. @Component
  4. struct StatePage {
  5. @State count: number = 0
  6. private toggleClick() {
  7. this.count += 1
  8. }
  9. build() {
  10. Row() {
  11. Column({ space: 20 }) {
  12. Button(`这是父组件,当前值: ${this.count}`)
  13. .fontSize(24)
  14. .onClick(this.toggleClick.bind(this))
  15. ComponentPage({ count: this.count })
  16. }
  17. .width('100%')
  18. }.height('100%')
  19. }
  20. }
描述:
  • 将父组件的count设置到子组件使用的@Prop修饰的变量时,父组件与子组件这时建立起单向同步
  • 父组件修改值后,子组件跟着修改,子组件修改值父组件不受影响
  • 使用的@Prop修饰的变量不能自己初始化
效果图:

@Link 父子双向同步

  • @Link装饰的变量和父组件构建双向同步关系的状态变量,父组件会接受来自@Link装饰的变量的修改的同步,父组件的更新也会同步给@Link装饰的变量。
  • @Link装饰的变量与其父组件中的数据源共享相同的值
  • @Link装饰器不能在@Entry装饰的自定义组件中使用
子组件:
  1. @Component
  2. export struct ComponentPage {
  3. @Link count: number
  4. private toggleClick() {
  5. this.count += 1
  6. }
  7. build() {
  8. Row() {
  9. Column({ space: 20 }) {
  10. Button(`这是子组件,${this.count}`)
  11. .fontSize(24)
  12. .onClick(this.toggleClick.bind(this))
  13. }
  14. .width('100%')
  15. }
  16. }
  17. }
父组件:
  1. import { ComponentPage } from "./ComponentPage"
  2. @Entry
  3. @Component
  4. struct StatePage {
  5. @State count: number = 0
  6. private toggleClick() {
  7. this.count += 1
  8. }
  9. build() {
  10. Row() {
  11. Column({ space: 20 }) {
  12. Button(`这是父组件,当前值: ${this.count}`)
  13. .fontSize(24)
  14. .onClick(this.toggleClick.bind(this))
  15. ComponentPage({ count: $count })
  16. }
  17. .width('100%')
  18. }.height('100%')
  19. }
  20. }
描述:
  • 父组件通过$count来和子组件的@Link修饰的值绑定
  • 绑定之后实现父子双向绑定,修改一端,另一组件也随之变化
  • 使用@Link不能自己初始化
效果图:

@State、@Prop与@Link的异同

相同点:

  • 都会引起UI重绘
  • 内部私有

不同点:

不同点@State@Prop@Link
装饰内容基本数据类型,类,数组基本数据类型基本数据类型,类,数组
关联不与其他控件关联父@State -> 子@Prop 单向关联父@State <-> 子@Link 双向关联
初始化时机声明时创建组件时由参数传入创建组件时由参数传入

四、生产消费的装饰器

@Provide、@Consume

后代通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步,与@State/@Link不同的是,前者可以在多层级的父子组件之间传递

案例:

在父组件中将数据多级传递给子组件,子子组件

1.使用@Link修饰的变量进行传递
父组件:
  1. import { ProviderSonPage } from "./ProviderSonPage"
  2. @Entry
  3. @Component
  4. struct ProviderPage {
  5. @State message: string = '父类A'
  6. build() {
  7. Row() {
  8. Column() {
  9. Text(this.message).fontSize(50).fontColor(Color.Red)
  10. .onClick(() => {
  11. //点击文字 进行切换
  12. this.message = this.message === '父类A' ? '父类B' : '父类A'
  13. })
  14. //调用子组件
  15. ProviderSonPage({ sonMsg: $message })
  16. }
  17. .width('100%')
  18. }
  19. .height('100%')
  20. }
  21. }
子组件:
  1. import { ProviderGrandSonPage } from "./ProviderGrandSonPage"
  2. @Component
  3. export struct ProviderSonPage {
  4. @Link sonMsg: string
  5. build() {
  6. Column() {
  7. Text(this.sonMsg).fontSize(30).fontColor(Color.Green)
  8. .onClick(() => {
  9. this.sonMsg = '我是子类'
  10. })
  11. //调用孙子组件:子类的子类
  12. ProviderGrandSonPage({ grandSonMsg: $sonMsg })
  13. }
  14. }
  15. }
子子组件:
  1. @Component
  2. export struct ProviderGrandSonPage {
  3. @Link grandSonMsg: string
  4. build() {
  5. Column() {
  6. Text(this.grandSonMsg).fontSize(20).fontColor(Color.Blue)
  7. .onClick(() => {
  8. this.grandSonMsg = '我是子类的子类'
  9. })
  10. }
  11. }
  12. }
总结:
  • 都需要通过一个多余被@Link修饰的变量进行传递,太过复杂,如果传递层级太深没更加明显
2.发布者订阅者模式

使用发布者Provide和订阅者Consume可以直接传递到子子组件

父组件:
  1. import { ProviderSonPage } from "./ProviderSonPage"
  2. @Entry
  3. @Component
  4. struct ProviderPage {
  5. @Provide('Mes') message: string = '父类A'
  6. //也可以写成@Provide message: string = '父类A'
  7. build() {
  8. Row() {
  9. Column() {
  10. Text(this.message).fontSize(50).fontColor(Color.Red)
  11. .onClick(() => {
  12. this.message = this.message === '父类A' ? '父类B' : '父类A'
  13. })
  14. //调用子组件时就不再需要传递参数
  15. ProviderSonPage()
  16. }
  17. .width('100%')
  18. }
  19. .height('100%')
  20. }
  21. }
子组件:
  1. import { ProviderGrandSonPage } from "./ProviderGrandSonPage"
  2. @Component
  3. export struct ProviderSonPage {
  4. @Consume('Mes') sonMsg:string
  5. build() {
  6. Column() {
  7. Text(this.sonMsg).fontSize(30).fontColor(Color.Green)
  8. .onClick(() => {
  9. this.sonMsg = '我是子类'
  10. })
  11. //调用子组件时就不再需要传递参数
  12. ProviderGrandSonPage()
  13. }
  14. }
  15. }
子子组件:
  1. @Component
  2. export struct ProviderGrandSonPage {
  3. @Consume('Mes') grandSonMsg:string
  4. //也可以写成@Consume message:string
  5. build() {
  6. Column() {
  7. Text(this.grandSonMsg).fontSize(20).fontColor(Color.Blue)
  8. .onClick(() => {
  9. this.grandSonMsg = '我是子类的子类'
  10. })
  11. }
  12. }
  13. }
总结:
  • 使用发布者订阅者模式,父类使用@Provide,其他需要观察的子类使用@Consume,就可以能实现双向绑定
  • 当层级很深时不需要一层一层的往下传递,直接使用发布者订阅者进行监听就能实现相同的效果
  • @Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,变量类型必须相同
  • @Provide必须设置初始值,@Consume不可设置默认初始值
  • @Provide修饰的变量和@Consume修饰的变量是一对多的关系

效果图:

五、状态变量更改通知

@Watch:使用观察者模式的装饰器,但该装饰器不是触发变量变化,而是绑定一个函数,当@Watch变量变化时,调用该函数

@Watch和自定义组件更新

子组件:
  1. @Component
  2. export struct TotalViewPage {
  3. @Prop @Watch('onCountUpdated') count: number;
  4. @State total: number = 0;
  5. // @Watch 回调
  6. onCountUpdated(propName: string): void {
  7. this.total += this.count;
  8. }
  9. build() {
  10. Text(`Total: ${this.total}`)
  11. }
  12. }
父组件:
  1. import {TotalViewPage} from "./TotalViewPage"
  2. @Entry
  3. @Component
  4. struct CountModifierPage {
  5. @State count: number = 0;
  6. build() {
  7. Column() {
  8. Button('add to basket')
  9. .onClick(() => {
  10. this.count++
  11. })
  12. TotalViewPage({ count: this.count })
  13. }
  14. }
  15. }
描述:
  1. CountModifier自定义组件的Button.onClick点击事件自增count
  2. 由于@State count变量更改,子组件TotalView中的@Prop被更新,其@Watch('onCountUpdated')方法被调用,更新了子组件TotalView 中的total变量
  3. 子组件TotalView中的Text重新渲染

@Watch与@Link组合使用

bean对象:PurchaseItem
  1. export class PurchaseItem {
  2. static NextId: number = 0;
  3. public id: number;
  4. public price: number;
  5. constructor(price: number) {
  6. this.id = PurchaseItem.NextId++;
  7. this.price = price;
  8. }
  9. }
子类:BasketViewer
  1. import {PurchaseItem} from "./PurchaseItem"
  2. @Component
  3. export struct BasketViewer {
  4. @Link @Watch('onBasketUpdated') shopBasket: PurchaseItem[];
  5. @State totalPurchase: number = 0;
  6. updateTotal(): number {
  7. let total = this.shopBasket.reduce((sum, i) => sum + i.price, 0);
  8. // 超过100欧元可享受折扣
  9. if (total >= 100) {
  10. total = 0.9 * total;
  11. }
  12. return total;
  13. }
  14. // @Watch 回调
  15. onBasketUpdated(propName: string): void {
  16. this.totalPurchase = this.updateTotal();
  17. }
  18. build() {
  19. Column() {
  20. ForEach(this.shopBasket,
  21. (item) => {
  22. Text(`Price: ${item.price.toFixed(2)} €`)
  23. },
  24. item => item.id.toString()
  25. )
  26. Text(`Total: ${this.totalPurchase.toFixed(2)} €`)
  27. }
  28. }
  29. }
父类:BasketModifierPage
  1. import {BasketViewer} from "./BasketViewer"
  2. import {PurchaseItem} from "./PurchaseItem"
  3. @Entry
  4. @Component
  5. struct BasketModifierPage {
  6. @State shopBasket: PurchaseItem[] = [];
  7. build() {
  8. Column() {
  9. Button('Add to basket')
  10. .onClick(() => {
  11. this.shopBasket.push(new PurchaseItem(Math.round(100 * Math.random())))
  12. })
  13. BasketViewer({ shopBasket: $shopBasket })
  14. }
  15. }
  16. }
描述:
  1. BasketModifierPage组件的Button.onClick向BasketModifier shopBasket中添加条目
  2. @Link装饰的BasketViewer shopBasket值发生变化
  3. 状态管理框架调用@Watch函数BasketViewer onBasketUpdated 更新BasketViewer TotalPurchase的值
  4. @Link shopBasket的改变,新增了数组项,ForEach组件会执行item Builder,渲染构建新的Item项;@State totalPurchase改变,对应的Text组件也重新渲染
  5. 重新渲染是异步发生的
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/714609
推荐阅读
相关标签
  

闽ICP备14008679号