鸿蒙系列--装饰器
一、基础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 = 0private 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 = 0private toggleClick() {this.count += 1}build() {Row() {Column({ space: 20 }) {Button(`这是父组件,当前值: ${this.count}`).fontSize(24).onClick(this.toggleClick.bind(this))//使用默认初始化值ComponentPage()//设置count初始值为:20ComponentPage({ count: 20 })}.width('100%')}.height('100%')}
}
描述:
- 当被点击之后修改了count的值,页面会重新绘制UI
- 子组件中的count和父组件的count互不影响
- 可以给子组件构造方法设置初始值
- 使用@State修饰的变量必须初始化
效果图:


@Prop 父子单向同步
- 继承@State的所有功能
- 被其装饰的变量可以和父组件建立单向同步关系。@Prop装饰的变量是可变的,但修改不会同步回父组件,当父组件的@State变化时,本地修改的@Prop会被覆盖
子组件:
@Component
export struct ComponentPage {@Prop count: numberprivate 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 = 0private 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: numberprivate 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 = 0private 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: stringbuild() {Column() {Text(this.sonMsg).fontSize(30).fontColor(Color.Green).onClick(() => {this.sonMsg = '我是子类'})//调用孙子组件:子类的子类ProviderGrandSonPage({ grandSonMsg: $sonMsg })}}
}
子子组件:
@Component
export struct ProviderGrandSonPage {@Link grandSonMsg: stringbuild() {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:stringbuild() {Column() {Text(this.sonMsg).fontSize(30).fontColor(Color.Green).onClick(() => {this.sonMsg = '我是子类'})//调用子组件时就不再需要传递参数ProviderGrandSonPage()}}
}
子子组件:
@Component
export struct ProviderGrandSonPage {@Consume('Mes') grandSonMsg:string//也可以写成@Consume message:stringbuild() {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 })}}
}
描述:
- CountModifier自定义组件的Button.onClick点击事件自增count
- 由于@State count变量更改,子组件TotalView中的@Prop被更新,其@Watch('onCountUpdated')方法被调用,更新了子组件TotalView 中的total变量
- 子组件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 })}}
}
描述:
- BasketModifierPage组件的Button.onClick向BasketModifier shopBasket中添加条目
- @Link装饰的BasketViewer shopBasket值发生变化
- 状态管理框架调用@Watch函数BasketViewer onBasketUpdated 更新BasketViewer TotalPurchase的值
- @Link shopBasket的改变,新增了数组项,ForEach组件会执行item Builder,渲染构建新的Item项;@State totalPurchase改变,对应的Text组件也重新渲染
- 重新渲染是异步发生的
相关文章:
鸿蒙系列--装饰器
一、基础UI组件结构 每个UI组件需要定义为Component struct对象,其内部必须包含一个且只能包含一个build(){}函数,用于绘制UI;struct之内、build()函数之外的地方用于存放数据。 二、基本UI装饰器 Entry 装饰struct,页面的入口…...
FairGuard游戏加固产品常见问题解答
针对日常对接中,各位用户对FairGuard游戏加固方案在安全性、稳定性、易用性、接入流程等方面的关注,我们梳理了相关问题与解答,希望可以让您对产品有一个初步的认知与认可。 Q1:FairGuard游戏加固产品都有哪些功能? A:FairGuar…...
Redis(二)数据类型
文章目录 官网备注十大数据类型StringListHashSetZSetBitmapHyperLogLog:GEOStreamBitfield 官网 英文:https://redis.io/commands/ 中文:http://www.redis.cn/commands.html 备注 命令不区分大小写,key区分大小写帮助命令help…...
2023年广东省网络安全B模块(笔记详解)
模块B 网络安全事件响应、数字取证调查和应用安全 一、项目和任务描述: 假定你是某网络安全技术支持团队成员,某企业的服务器系统被黑客攻击,你的团队前来帮助企业进行调查并追踪本次网络攻击的源头,分析黑客的攻击方式,发现系统漏洞,提交网络安全事件响应报告,修复系统…...
每日力扣算法题(简单篇)
543.二叉树的直径 原题: 给你一棵二叉树的根节点,返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 解题思路: …...
Flume基础知识(三):Flume 实战监控端口数据官方案例
1. 监控端口数据官方案例 1)案例需求: 使用 Flume 监听一个端口,收集该端口数据,并打印到控制台。 2)需求分析: 3)实现步骤: (1)安装 netcat 工具 sudo yum …...
通过IP地址如何进行网络安全防护
IP地址在网络安全防护中起着至关重要的作用,可以用于监控、过滤和控制网络流量,识别潜在威胁并加强网络安全。以下是通过IP地址进行网络安全防护的一些建议: 1. 建立IP地址白名单和黑名单: 白名单:确保只有授权的IP地…...
Vue.js 中使用 Watch 选项实现动态问题判断与展示答案
组件结构 以下是组件的基本结构: <template><div><!-- 输入框,用于输入问题 --><p>提出一个是/否问题:<input v-model"question" :disabled"loading" /></p><!-- 显示答案 --&…...
python笔记-自用
2024/1/3# python用号实现字符串的拼接,非字符串不能拼接 from pymysql import Connection# 连接mysql数据库salary 100 name "wang"ans "%s" % salary name print(ans)x 1 y 2 sum "%s %s" % (x, y) print(sum)# %s字符串占…...
安克创新与火山引擎数智平台开展合作:数据分析降门槛 数据协同破边界
更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 近日,消费电子品牌安克创新与火山引擎数智平台(VeDI)达成合作,双方将聚焦安克创新大数据平台的海量数据分析场景&…...
LDD学习笔记 -- Linux内核模块
LDD学习笔记 -- 内核模块 简介LKM类型Static Linux Kernel ModuleDynamic Linux Kernel ModuleLKM编写语法 syntax详细描述内核头文件用户空间头文件Module Initialization FunctionModule Cleanup FunctionKeyword & Tag宏 __init __exitLKM入口注册Module Metadate&#…...
springboot整合springbatch批处理
springboot整合springbatch实现批处理 简介项目搭建步骤 简介 项目搭建 参考博客【场景实战】Spring Boot Spring Batch 实现批处理任务,保姆级教程 步骤 1.建表 建表sql CREATE TABLE student (id int NOT NULL AUTO_INCREMENT,name varchar(100) NOT NULL C…...
答案解析——C语言—第2次作业:转义字符
本次作业的链接如下:C语言—第2次作业:转义字符 1.下面哪个不是C语言内置的数据类型: C char //字符数据类型short //短整型int //整形long //长整型long long //更长的整形float //单精度浮点数double //双精度浮点数 …...
HTML5-新增表单input属性
新增表单属性 form控件主要新增的属性: autocomplete 是否启用表单的自动完成功能,取值:on(默认)、off novalidate 提交表单时不进行校验,默认会进行表单校验 autocomplete属性 概念:autocomplete属性…...
css-、串联选择器和后代选择器的用法
& &表示嵌套的上一级,这是sass的语法,代表上一级选择器 .btn {&.primary {background-color: #007bff;color: #fff;} } 编译出来的结果是同一个元素,有两个类名,两个类名之间没有空格: .btn.primary {…...
nifi详细介绍--一款开箱即用、功能强大可靠,可用于处理和分发数据的大数据组件
目录 目录 一、引言 二、NiFi 的历史背景介绍 三、NiFi 是什么? 核心特性 应用领域 四、NIFI 入门 五 、NiFi 工作流程 六、实际应用场景 七、优势总结 一、引言 NiFi(Apache NiFi),全名为“Niagara Files”࿰…...
K8S Dashboard登录Token过期问题处理
整体思路 用户访问一个页面,在该页面中设置一个超链接,点击跳转至K8S Dashboard;跳转后,使用剪贴板上已复制的Token粘贴到Dashboard页面中的输入框登录即可。 写个定时任务将Token复制到页面上,过期了重新再登…...
x-cmd pkg | trafilatura - 网络爬虫和搜索引擎优化工具
目录 简介首次用户技术特点竞品和相关作品进一步阅读 简介 trafilatura 是一个用于从网页上提取文本的命令行工具和 python 包: 提供网络爬虫、下载、抓取以及提取主要文本、元数据和评论等功能可帮助网站导航和从站点地图和提要中提取链接无需数据库,输出即可转换…...
前端知识点(面试可看) —— JS
摘要 马上就要毕业啦,没有参加2023年的秋招,准备在最近开始找全职或者实习工作,然后也马上过年了,总结和理一下自己的知识要点,参加2024年的春招。 1. JS的执行流程 浏览器的V8引擎收到到执行的JS代码V8结构化这段代…...
CSRF总结
CSRF 文章目录 CSRF漏洞原理漏洞危害漏洞防护CSRF攻击流程CSRF和XSS的区别CSRF漏洞挖掘及利用 CSRF 跨站点请求伪造(CSRF)攻击者会诱导受害者点击事先伪造好的url或者链接,点击后,攻击者就可以盗用你的身份,以你的身份…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
