鸿蒙系列--装饰器

一、基础UI组件结构

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

二、基本UI装饰器

@Entry

装饰struct,页面的入口

@Component

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

@Entry
@Component
struct TestPage {
    build() {
       ……
    }
}

三、数据装饰器

@State 父子相互独立

  • 装饰的变量是组件的局部变量,必须本地初始化,可通过构造参数赋值
  • 当该数据被修改时,所在组件的build()方法会被重新调用,会重新绘制所在UI
子组价:
@Component
export struct ComponentPage {
  @State count: number = 0

  private toggleClick() {
    this.count += 1
  }

  build() {
    Row() {
      Column({ space: 20 }) {
        Button(`这是子组件,${this.count}`)
          .fontSize(24)
          .onClick(this.toggleClick.bind(this))
      }
      .width('100%')
    }
  }
}
父组件:
import { ComponentPage } from "./ComponentPage"

@Entry
@Component
struct StatePage {
  @State count: number = 0

  private toggleClick() {
    this.count += 1
  }

  build() {
    Row() {
      Column({ space: 20 }) {
        Button(`这是父组件,当前值: ${this.count}`)
          .fontSize(24)
          .onClick(this.toggleClick.bind(this))

        //使用默认初始化值
        ComponentPage()
        //设置count初始值为:20
        ComponentPage({ count: 20 })
      }
      .width('100%')
    }.height('100%')
  }
}
描述:
  • 当被点击之后修改了count的值,页面会重新绘制UI
  • 子组件中的count和父组件的count互不影响
  • 可以给子组件构造方法设置初始值
  • 使用@State修饰的变量必须初始化
效果图:

@Prop 父子单向同步

  • 继承@State的所有功能
  • 被其装饰的变量可以和父组件建立单向同步关系。@Prop装饰的变量是可变的,但修改不会同步回父组件,当父组件的@State变化时,本地修改的@Prop会被覆盖
子组件:
@Component
export struct ComponentPage {
  @Prop count: number

  private toggleClick() {
    this.count += 1
  }

  build() {
    Row() {
      Column({ space: 20 }) {
        Button(`这是子组件,${this.count}`)
          .fontSize(24)
          .onClick(this.toggleClick.bind(this))
      }
      .width('100%')
    }
  }
}
父组件:
import { ComponentPage } from "./ComponentPage"

@Entry
@Component
struct StatePage {
  @State count: number = 0

  private toggleClick() {
    this.count += 1
  }

  build() {
    Row() {
      Column({ space: 20 }) {
        Button(`这是父组件,当前值: ${this.count}`)
          .fontSize(24)
          .onClick(this.toggleClick.bind(this))

        ComponentPage({ count: this.count })
      }
      .width('100%')
    }.height('100%')
  }
}
描述:
  • 将父组件的count设置到子组件使用的@Prop修饰的变量时,父组件与子组件这时建立起单向同步
  • 父组件修改值后,子组件跟着修改,子组件修改值父组件不受影响
  • 使用的@Prop修饰的变量不能自己初始化
效果图:

@Link 父子双向同步

  • @Link装饰的变量和父组件构建双向同步关系的状态变量,父组件会接受来自@Link装饰的变量的修改的同步,父组件的更新也会同步给@Link装饰的变量。
  • @Link装饰的变量与其父组件中的数据源共享相同的值
  • @Link装饰器不能在@Entry装饰的自定义组件中使用
子组件:
@Component
export struct ComponentPage {
  @Link count: number

  private toggleClick() {
    this.count += 1
  }

  build() {
    Row() {
      Column({ space: 20 }) {
        Button(`这是子组件,${this.count}`)
          .fontSize(24)
          .onClick(this.toggleClick.bind(this))
      }
      .width('100%')
    }
  }
}
父组件:
import { ComponentPage } from "./ComponentPage"

@Entry
@Component
struct StatePage {
  @State count: number = 0

  private toggleClick() {
    this.count += 1
  }

  build() {
    Row() {
      Column({ space: 20 }) {
        Button(`这是父组件,当前值: ${this.count}`)
          .fontSize(24)
          .onClick(this.toggleClick.bind(this))

        ComponentPage({ count: $count })
      }
      .width('100%')
    }.height('100%')
  }
}
描述:
  • 父组件通过$count来和子组件的@Link修饰的值绑定
  • 绑定之后实现父子双向绑定,修改一端,另一组件也随之变化
  • 使用@Link不能自己初始化
效果图:

@State、@Prop与@Link的异同

相同点:

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

不同点:

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

四、生产消费的装饰器

@Provide、@Consume

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

案例:

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

1.使用@Link修饰的变量进行传递
父组件:
import { ProviderSonPage } from "./ProviderSonPage"

@Entry
@Component
struct ProviderPage {
  @State message: string = '父类A'

  build() {
    Row() {
      Column() {
        Text(this.message).fontSize(50).fontColor(Color.Red)
          .onClick(() => {
            //点击文字  进行切换
            this.message = this.message === '父类A' ? '父类B' : '父类A'
          })
        //调用子组件
        ProviderSonPage({ sonMsg: $message })
      }
      .width('100%')
    }
    .height('100%')
  }
}
子组件:
import { ProviderGrandSonPage } from "./ProviderGrandSonPage"

@Component
export struct ProviderSonPage {
  @Link sonMsg: string

  build() {
    Column() {
      Text(this.sonMsg).fontSize(30).fontColor(Color.Green)
        .onClick(() => {
          this.sonMsg = '我是子类'
        })
      //调用孙子组件:子类的子类
      ProviderGrandSonPage({ grandSonMsg: $sonMsg })
    }
  }
}
子子组件:
@Component
export struct ProviderGrandSonPage {
  @Link grandSonMsg: string

  build() {
    Column() {
      Text(this.grandSonMsg).fontSize(20).fontColor(Color.Blue)
        .onClick(() => {
          this.grandSonMsg = '我是子类的子类'
        })
    }
  }
}
总结:
  • 都需要通过一个多余被@Link修饰的变量进行传递,太过复杂,如果传递层级太深没更加明显
2.发布者订阅者模式

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

父组件:
import { ProviderSonPage } from "./ProviderSonPage"

@Entry
@Component
struct ProviderPage {
  @Provide('Mes') message: string = '父类A'
  //也可以写成@Provide message: string = '父类A'
  build() {
    Row() {
      Column() {
        Text(this.message).fontSize(50).fontColor(Color.Red)
          .onClick(() => {

            this.message = this.message === '父类A' ? '父类B' : '父类A'
          })
        //调用子组件时就不再需要传递参数
        ProviderSonPage()
      }
      .width('100%')
    }
    .height('100%')
  }
}
子组件:
import { ProviderGrandSonPage } from "./ProviderGrandSonPage"

@Component
export struct ProviderSonPage {
  @Consume('Mes') sonMsg:string

  build() {
    Column() {
      Text(this.sonMsg).fontSize(30).fontColor(Color.Green)
        .onClick(() => {
          this.sonMsg = '我是子类'
        })
      //调用子组件时就不再需要传递参数
      ProviderGrandSonPage()
    }
  }
}
子子组件:
@Component
export struct ProviderGrandSonPage {
  @Consume('Mes') grandSonMsg:string
  //也可以写成@Consume message:string

  build() {
    Column() {
      Text(this.grandSonMsg).fontSize(20).fontColor(Color.Blue)
        .onClick(() => {
          this.grandSonMsg = '我是子类的子类'
        })
    }
  }
}
总结:
  • 使用发布者订阅者模式,父类使用@Provide,其他需要观察的子类使用@Consume,就可以能实现双向绑定
  • 当层级很深时不需要一层一层的往下传递,直接使用发布者订阅者进行监听就能实现相同的效果
  • @Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,变量类型必须相同
  • @Provide必须设置初始值,@Consume不可设置默认初始值
  • @Provide修饰的变量和@Consume修饰的变量是一对多的关系

效果图:

五、状态变量更改通知

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

@Watch和自定义组件更新

子组件:
@Component
export struct TotalViewPage {
  @Prop @Watch('onCountUpdated') count: number;
  @State total: number = 0;
  // @Watch 回调
  onCountUpdated(propName: string): void {
    this.total += this.count;
  }

  build() {
    Text(`Total: ${this.total}`)
  }
}
父组件:
import {TotalViewPage} from "./TotalViewPage"

@Entry
@Component
struct CountModifierPage {
  @State count: number = 0;

  build() {
    Column() {
      Button('add to basket')
        .onClick(() => {
          this.count++
        })
      TotalViewPage({ count: this.count })
    }
  }
}
描述:
  1. CountModifier自定义组件的Button.onClick点击事件自增count
  2. 由于@State count变量更改,子组件TotalView中的@Prop被更新,其@Watch('onCountUpdated')方法被调用,更新了子组件TotalView 中的total变量
  3. 子组件TotalView中的Text重新渲染

@Watch与@Link组合使用

bean对象:PurchaseItem
export class PurchaseItem {
  static NextId: number = 0;
  public id: number;
  public price: number;

  constructor(price: number) {
    this.id = PurchaseItem.NextId++;
    this.price = price;
  }
}
子类:BasketViewer
import {PurchaseItem} from "./PurchaseItem"

@Component
export struct BasketViewer {
  @Link @Watch('onBasketUpdated') shopBasket: PurchaseItem[];
  @State totalPurchase: number = 0;

  updateTotal(): number {
    let total = this.shopBasket.reduce((sum, i) => sum + i.price, 0);
    // 超过100欧元可享受折扣
    if (total >= 100) {
      total = 0.9 * total;
    }
    return total;
  }
  // @Watch 回调
  onBasketUpdated(propName: string): void {
    this.totalPurchase = this.updateTotal();
  }
  build() {
    Column() {
      ForEach(this.shopBasket,
        (item) => {
          Text(`Price: ${item.price.toFixed(2)} €`)
        },
        item => item.id.toString()
      )
      Text(`Total: ${this.totalPurchase.toFixed(2)} €`)
    }
  }
}
父类:BasketModifierPage
import {BasketViewer} from "./BasketViewer"
import {PurchaseItem} from "./PurchaseItem"

@Entry
@Component
struct BasketModifierPage {
  @State shopBasket: PurchaseItem[] = [];
  build() {
    Column() {
      Button('Add to basket')
        .onClick(() => {
          this.shopBasket.push(new PurchaseItem(Math.round(100 * Math.random())))
        })
      BasketViewer({ shopBasket: $shopBasket })
    }
  }
}
描述:
  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. 重新渲染是异步发生的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/290625.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

系列十、Spring Cloud Gateway

一、Spring Cloud Gateway 1.1、概述 Spring Cloud全家桶中有个很重要的组件就是网关&#xff0c;在1.x版本中采用的是Zuul网关&#xff0c;但是在2.x版本中&#xff0c;由于Zuul的升级一直跳票&#xff0c;Spring Cloud最后自己研发了一个网关替代Zuul&#xff0c;即&#xf…

2020年认证杯SPSSPRO杯数学建模B题(第二阶段)分布式无线广播全过程文档及程序

2020年认证杯SPSSPRO杯数学建模 B题 分布式无线广播 原题再现&#xff1a; 以广播的方式来进行无线网通信&#xff0c;必须解决发送互相冲突的问题。无线网的许多基础通信协议都使用了令牌的方法来解决这个问题&#xff0c;在同一个时间段内&#xff0c;只有唯一一个拿到令牌…

Unity 2022 版本 寻路 NavMesh

官方教程地址 https://docs.unity3d.com/Packages/com.unity.ai.navigation1.1/manual/index.html 首先装包 先给地图 和 阻挡 设置为静态 然后给地上行走的地方 添加组件 可以直接bake 然后会显示蓝色的可行走路径 player 添加插件 然后给角色添加脚本 using System.Co…

MySQL基础篇(一)SQL

视频地址: 黑马程序员 MySQL数据库入门到精通&#xff0c;从mysql安装到mysql高级、mysql优化全囊括 SQL&#xff0c;全称 Structured Query Language&#xff0c;结构化查询语言。操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一 标准。 一、SQL通用语…

ImportError: DLL load failed while importing imaging: 找不到指定的模块

24.1 ImportError: DLL load failed while importing imaging: 找不到指定的模块 参考&#xff1a;https://zhuanlan.zhihu.com/p/662305030?utm_id0 说明&#xff1a;Pillow 与pillow都可以&#xff0c;不区分首字母大小写 第一步&#xff1a;查看是否有pillow 1&#xff09;…

AI:117-基于机器学习的环境污染影响评估

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

vue3+Ts+Hook的方式实现商城核心功能sku选择器

前言 Hooks是React等函数式编程框架中非常受欢迎的工具&#xff0c;随着VUE3 Composition API 函数式编程风格的推出&#xff0c;现在也受到越来越多VUE3开发者的青睐&#xff0c;它让开发者的代码具有更高的复用度且更加清晰、易于维护。 本文将通过CRMEB商城商品详情sku选择…

【完整思路】2023 年中国高校大数据挑战赛 赛题 B DNA 存储中的序列聚类与比对

2023 年中国高校大数据挑战赛 赛题 B DNA 存储中的序列聚类与比对 任务 1.错误率和拷贝数分析&#xff1a;分析“train_reads.txt”和“train_reference.txt”数据集中的错误率&#xff08;插入、删除、替换、链断裂&#xff09;和序列拷贝数。 2.聚类模型开发&#xff1a;开发…

Vue3+TS+ElementPlus的安装和使用教程【详细讲解】

前言 本文简单的介绍一下vue3框架的搭建和有关vue3技术栈的使用。通过本文学习我们可以自己独立搭建一个简单项目和vue3的实战。 随着前端的日月更新&#xff0c;技术的不断迭代提高&#xff0c;如今新vue项目首选用vue3 typescript vite pinia……模式。以前我们通常使用…

【教学类-43-15】 20240103 (5宫格数独:内存数据不够计算) 不重复的基础模板数量:未知

背景需求&#xff1a; 测试5宫格有多少种不重复的基础模板&#xff08;只测试所有的25数字一组有多少个&#xff09; # 测试11*11格&#xff0c;2*2一共4套3*3 宫格目的&#xff1a;数独14 5宫格有不同的基础模板 作者&#xff1a;阿夏 时间&#xff1a;2024年01月04日 13:…

【Echarts实践案例】如何在线图上标记一个非轴线上的点

需求背景&#xff1a; 当前有一个趋势图&#xff0c;横坐标表示灯泡平均使用时长&#xff0c;纵坐标表示灯泡平均使用温度。现在需要在当前坐标系下标记一个正在使用中的灯泡的时长及温度&#xff08;趋势图表示的是计算出的平均温度&#xff0c;所以当前灯泡的温度可能不会在…

算法导论复习——CHP22 基本图算法

图的表示 邻接矩阵和邻接表 稀疏图一般用邻接表表示&#xff08;稀疏图&#xff1a;边数|E|远小于的图 &#xff09; 稠密图更倾向于用邻接矩阵表示 (稠密图&#xff1a;边数|E|接近的图) 邻接矩阵可用于需要快速判断任意两个结点之间是否有边相连的应用场景。 如果用邻…

纯前端上传word,xlsx,ppt,在前端预览并下载成图片(预览效果可以,下载图片效果不太理想)

纯前端上传word,xlsx,ppt,在前端预览并下载成图片&#xff08;预览效果可以&#xff0c;下载图片效果不太理想&#xff09; 一.安装依赖二、主要代码 预览效果链接: https://github.com/501351981/vue-office 插件文档链接: https://501351981.github.io/vue-office/examples/d…

使用(?<!pattern) 负向后行断言正则表达式提取一个双引号开头和结尾的字符串

如下是一段java代码&#xff0c;我想用正则表达从中提取代码中的字符串 cond_buffer.append(" ORDER BY \"name\" \"").append(join(order_by_column,"\","));java是通过前后用双引号包含定义字符串的。但简单使用正则表达式".…

Kubernetes Gateway API V1.0:您应该切换吗?

自Kubernetes Gateway API 发布 v1.0以来已经过去两个多月了&#xff0c;这标志着其一些关键 API 已经进入普遍可用状态。 去年&#xff0c;当网关 API升级为测试版时&#xff0c;我曾写过有关该 API的文章&#xff0c;但一年后&#xff0c;问题仍然存在。您是否应该从 Ingres…

Python----matplotlib库

目录 plt库的字体&#xff1a; plt的操作绘图函数&#xff1a; plt.figure(figsizeNone, facecolorNone): plt.subplot(nrows, ncols, plot_number)&#xff1a; plt.axes(rect)&#xff1a; plt.subplots_adjust(): plt的读取和显示相关函数&#xff1a; plt库的基础图…

Python内置类属性__module__属性的使用教程

概要 在Python中&#xff0c;每个对象都有一些内置的属性&#xff0c;这些属性提供了有关对象的一些信息。其中一个内置属性是__module__属性。__module__属性是一个字符串&#xff0c;它表示定义了类或函数的模块的名称。在本篇文章中&#xff0c;我们将详细介绍__module__属…

随机森林,Random Forests Classifiers/Regressor

目录 介绍&#xff1a; 一、 Random Forests Classifiers&#xff08;离散型&#xff09; 1.1 数据处理 1.2建模 1.3特征值权值分析 1.4 特征值的缩减 二、Random Forests Regressor&#xff08;连续型&#xff09; 2.1数据处理 2.2建模 2.3调参 介绍&#xff1a; …

数据库:基础SQL知识+SQL实验1

&#xff08;1&#xff09;基础知识&#xff1a; 1.创建数据库&#xff1a; CREATE DATABASE <database_name> 2.删除数据库&#xff1a; DROP DATABASE <database_name> 3.相关数据类型&#xff1a; [1] 字符串类型 CHAR(n)&#xff1a;固定长度的字符数据…

基于ssm的《数据库系统原理》课程平台的设计与实现论文

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…