HarmonyOS:@LocalBuilder装饰器: 维持组件父子关系
一、前言
当开发者使用@Builder做引用数据传递时,会考虑组件的父子关系,使用了bind(this)之后,组件的父子关系和状态管理的父子关系并不一致。为了解决组件的父子关系和状态管理的父子关系保持一致的问题,引入@LocalBuilder装饰器。@LocalBuilder拥有和局部@Builder相同的功能,且比局部@Builder能够更好的确定组件的父子关系和状态管理的父子关系。
在阅读本文档前,建议提前阅读:@Builder。
说明
从API version 12开始支持。
二、装饰器使用说明
2.1 自定义组件内自定义构建函数
定义的语法:
@LocalBuilder MyBuilderFunction() { ... }
使用方法
this.MyBuilderFunction()
- 允许在自定义组件内定义一个或多个@LocalBuilder方法,该方法被认为是该组件的私有、特殊类型的成员函数。
- 自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用,但不允许在组件外调用。
- 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。
三、限制条件
- @LocalBuilder只能在所属组件内声明,不允许全局声明。
- @LocalBuilder不能被内置装饰器和自定义装饰器使用。
- 自定义组件内的静态方法不能和@LocalBuilder一起使用。
四、@LocalBuilder和局部@Builder使用区别
@Builder方法引用传参时,为了改变this指向,使用bind(this)后,会导致组件的父子关系和状态管理的父子关系不一致,但
@LocalBuilder是否使用bind(this),都不会改变组件的父子关系。@LocalBuilder和@Builder区别说明。
五、参数传递规则
@LocalBuilder函数的参数传递有按值传递和按引用传递两种,均需遵守以下规则:
- 参数的类型必须与参数声明的类型一致,不允许undefined、null和返回undefined、null的表达式。
- 在@LocalBuilder修饰的函数内部,不允许改变参数值。
- @LocalBuilder内UI语法遵循UI语法规则。
- 只有传入一个参数,且参数需要直接传入对象字面量才会按引用传递该参数,其余传递方式均为按值传递。
5.1 按引用传递参数
按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@LocalBuilder方法内的UI刷新。
若子组件调用父组件的@LocalBuilder函数,传入的参数发生变化,不会引起@LocalBuilder方法内的UI刷新。
使用场景:
组件TestLocalBuilder内的@LocalBuilder方法在build函数内调用,按键值对写法进行传值,当点击Click me 时,@LocalBuilder内的Text文本内容会随着状态变量内容的改变而改变。
class ReferenceType {paramString: string = '';
}@Entry
@Component
struct TestLocalBuilder {@State variableValue: string = 'Hello World';@LocalBuilderciteLocalBuilder(params: ReferenceType) {Row() {Text(`UseStateVarByReference: ${params.paramString} `)}};build() {Column({ space: 10 }) {this.citeLocalBuilder({ paramString: this.variableValue });Button('Click me').onClick(() => {this.variableValue = 'Hi World';})}.padding(20)}
}
效果图
按引用传递参数时,如果在@LocalBuilder方法内调用自定义组件,ArkUI提供$$作为按引用传递参数的范式。
使用场景:
组件TestLocalBuilder1内的@LocalBuilder方法内调用自定义组件,且按照引用传递参数将值传递到自定义组件,当Parent组件内状态变量值发生变化时,@LocalBuilder方法内的自定义组件HelloComponent的message值也会发生变化。
class ReferenceType2 {paramString: string = '';
}@Component
struct HelloComponent22 {@Prop message: string;build() {Row() {Text(`HelloComponent===${this.message}`);}}
}@Entry
@Component
struct TestLocalBuilder2 {@State variableValue: string = 'Hello World';@LocalBuilderciteLocalBuilder($$: ReferenceType2) {Row() {Column() {Text(`citeLocalBuilder===${$$.paramString}`);HelloComponent22({ message: $$.paramString });}}}build() {Column({ space: 10 }) {this.citeLocalBuilder({ paramString: this.variableValue });Button('Click me').onClick(() => {this.variableValue = 'Hi World';})}}
}
效果图
子组件引用父组件的@LocalBuilder函数,传入的参数为状态变量,状态变量的改变不会引发@LocalBuilder方法内的UI刷新,原因是@Localbuilder装饰的函数绑定在父组件上,状态变量刷新机制是刷新本组件以及其子组件,对父组件无影响,故无法引发刷新。若使用@Builder修饰则可引发刷新,原因是@Builder改变了函数的this指向,此时函数被绑定到子组件上,故能引发UI刷新。
使用场景:
组件Child将@State修饰的label值按照函数传参方式传递到Parent的@Builder和@LocalBuilder函数内,在被@Builder修饰的函数内,this指向Child,参数变化能引发UI刷新,在被@LocalBuilder修饰的函数内,this指向Parent,参数变化不能引发UI刷新。
class LayoutSize3 {size:number = 0;
}@Entry
@Component
struct TestLocalBuilder3 {label:string = 'parent';@State layoutSize:LayoutSize3 = {size:0};@LocalBuilder// @BuildercomponentBuilder($$:LayoutSize3) {Text(`${'this :'+this.label}`);Text(`${'size :'+$$.size}`);}build() {Column() {Child33({contentBuilder: this.componentBuilder });}}
}@Component
struct Child33 {label:string = 'child';@BuilderParam contentBuilder:((layoutSize: LayoutSize3) => void);@State layoutSize:LayoutSize3 = {size:0};build() {Column() {this.contentBuilder({size: this.layoutSize.size});Button("add child size").onClick(()=>{this.layoutSize.size += 1;})}}
}
效果图
使用@Builder参数变化可以引发UI刷新
六、按值传递参数
调用@LocalBuilder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@LocalBuilder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递。
使用场景:
组件Parent将@State修饰的label值按照函数传参方式传递到@LocalBuilder函数内,此时@LocalBuilder函数获取到的值为普通变量值,所以改变@State修饰的label值时,@LocalBuilder函数内的值不会发生改变。
@Entry
@Component
struct TestLocalBuilder4 {@State label: string = 'Hello';@LocalBuilderciteLocalBuilder(paramA1: string) {Row() {Text(`UseStateVarByValue: ${paramA1} `)}}build() {Column({space: 10}) {this.citeLocalBuilder(this.label);Button("点击改变@State修饰的label值").onClick(()=>{this.label = "word";console.log(`点击 改变@State修饰的label值 , this.label = ${ this.label}`);})}}
}
效果图
七、@LocalBuilder和@Builder区别说明
函数componentBuilder被@Builder修饰时,显示效果是 “Child”,函数componentBuilder被@LocalBuilder修饰时,显示效果是“Parent”。
说明:
@Builder componentBuilder()通过this.componentBuilder的形式传给子组件@BuilderParam customBuilderParam,this指向在Child的label,即“Child”。
@LocalBuilder componentBuilder()通过this.componentBuilder的形式传给子组件@BuilderParam customBuilderParam,this指向Parent的label,即“Parent”。
@Component
struct Child5 {label: string = `Child5`;@BuilderParam customBuilderParam: () => void;build() {Column() {this.customBuilderParam()}}
}@Entry
@Component
struct TestLocalBuilder5 {label: string = `Parent`;@Builder componentBuilder() {Text(`${this.label}`)}// @LocalBuilder componentBuilder() {// Text(`${this.label}`)// }build() {Column() {Child5({ customBuilderParam: this.componentBuilder })}}
}
函数componentBuilder被@Builder修饰时,显示效果是 “Child”效果图
函数componentBuilder被@LocalBuilder修饰时,显示效果是“Parent”效果图
八、使用场景
@LocalBuilder在@ComponentV2修饰的自定义组件中使用
使用局部的@LocalBuilder在@ComponentV2修饰的自定义组件中调用,修改变量触发UI刷新。
@ObservedV2
class Info6 {@Trace name: string = '';@Trace age: number = 0;
}@ComponentV2
struct ChildPage6 {@Require @Param childInfo: Info6;build() {Column() {Text(`自定义组件 name :${this.childInfo.name}`).fontSize(20).fontWeight(FontWeight.Bold)Text(`自定义组件 age :${this.childInfo.age}`).fontSize(20).fontWeight(FontWeight.Bold)}}
}@Entry
@ComponentV2
struct TestLocalBuilder6 {info1: Info = { name: "Tom", age: 25 };@Local info2: Info = { name: "Tom", age: 25 };@LocalBuilderprivateBuilder() {Column() {Text(`局部LocalBuilder@Builder name :${this.info1.name}`).fontSize(20).fontWeight(FontWeight.Bold)Text(`局部LocalBuilder@Builder age :${this.info1.age}`).fontSize(20).fontWeight(FontWeight.Bold)}}@LocalBuilderprivateBuilderSecond() {Column() {Text(`局部LocalBuilder@Builder name :${this.info2.name}`).fontSize(20).fontWeight(FontWeight.Bold)Text(`局部LocalBuilder@Builder age :${this.info2.age}`).fontSize(20).fontWeight(FontWeight.Bold)}}build() {Column() {Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1.fontSize(30).fontWeight(FontWeight.Bold)this.privateBuilder() // 调用局部@BuilderLine().width('100%').height(10).backgroundColor('#000000').margin(10)Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text1.fontSize(30).fontWeight(FontWeight.Bold)this.privateBuilderSecond() // 调用局部@BuilderLine().width('100%').height(10).backgroundColor('#000000').margin(10)Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1.fontSize(30).fontWeight(FontWeight.Bold)ChildPage6({ childInfo: this.info1}) // 调用自定义组件Line().width('100%').height(10).backgroundColor('#000000').margin(10)Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text2.fontSize(30).fontWeight(FontWeight.Bold)ChildPage6({ childInfo: this.info2}) // 调用自定义组件Line().width('100%').height(10).backgroundColor('#000000').margin(10)Button("change info1&info2").onClick(() => {this.info1 = { name: "Cat", age: 18} // Text1不会刷新,原因是没有装饰器修饰监听不到值的改变。this.info2 = { name: "Cat", age: 18} // Text2会刷新,原因是有装饰器修饰,可以监听到值的改变。})}}
}
效果图
相关文章:
HarmonyOS:@LocalBuilder装饰器: 维持组件父子关系
一、前言 当开发者使用Builder做引用数据传递时,会考虑组件的父子关系,使用了bind(this)之后,组件的父子关系和状态管理的父子关系并不一致。为了解决组件的父子关系和状态管理的父子关系保持一致的问题,引入LocalBuilder装饰器。…...
YOLOv10-1.1部分代码阅读笔记-downloads.py
downloads.py ultralytics\utils\downloads.py 目录 downloads.py 1.所需的库和模块 2.def is_url(url, checkFalse): 3.def delete_dsstore(path, files_to_delete(".DS_Store", "__MACOSX")): 4.def zip_directory(directory, compressTrue, ex…...
计算机图形学【绘制立方体和正六边形】
工具介绍 OpenGL:一个跨语言的图形API,用于渲染2D和3D图形。它提供了绘制图形所需的底层功能。 GLUT:OpenGL的一个工具库,简化了窗口创建、输入处理和其他与图形环境相关的任务。 使用的函数 1. glClear(GL_COLOR_BUFFER_BIT |…...
基于django中医药数据可视化平台(源码+lw+部署文档+讲解),源码可白嫖!
摘要 时代在飞速进步,每个行业都在努力发展现在先进技术,通过这些先进的技术来提高自己的水平和优势,中医药管理平台当然不能排除在外。中医药数据可视化平台是在实际应用和软件工程的开发原理之上,运用Python语言、ECharts技术、…...
kafka消费堆积问题探索
背景 我们的商城项目用PHP写的,原本写日志方案用的是PHP的方案,但是,这个方案导致资源消耗一直降不下来,使用了20个CPU。后面考虑使用通过kafka的方案写日志,商城中把产生的日志丢到kafka中,在以go写的项目…...
Vue.js 使用插槽(Slots)优化组件结构
Vue.js 使用插槽(Slots)优化组件结构 今天我们聊聊 Vue.js 的一个超实用功能——插槽(Slots)。插槽是 Vue 组件开发中的神器,用好它,你可以让组件变得更灵活、更可复用,还能写出优雅的代码结构…...
Broker如何进行定时心跳发送和故障感知
1.前言 此文章是在儒猿课程中的学习笔记,感兴趣的想看原来的课程可以去咨询儒猿课堂《从0开始带你成为RocketMQ高手》,我本人觉得这个作者还是不错,都是从场景来进行分析,感觉还是挺适合我这种小白的。这块主要都是我自己的学习笔…...
网络安全设备主要有什么
网络安全设备指的肯定是硬件设备了,国内卖安全硬件的没几家,天融信,启明星辰,绿盟,深信服,就这四家卖的比较齐全吧,上它们官网看一下,就知道市面上主要的网络安全设备有哪些了。分类…...
Android Framework WMS全面概述和知识要点
一、概述 定义与作用 在 Android 系统中,WindowManagerService(WMS)就像是一个大管家,负责管理整个系统的窗口界面。它是 Android Framework 的核心组件之一,处于 system_server 进程内,在 Framework 层占…...
记一次某红蓝演练经历
在某天接到任务,对xxx进行一次红蓝演练,于是把自己渗透过程给记录下来,漏洞关键地方也会打码,希望各位大佬理解,菜鸡一枚,勿喷/(ㄒoㄒ)/~~ 概述 拿到目标域名第一件事就是信息收集,曾经一位大…...
一个运行在浏览器中的开源Web操作系统Puter本地部署与远程访问
文章目录 前言1.关于Puter2.本地部署Puter3.Puter简单使用4. 安装内网穿透5.配置puter公网地址6. 配置固定公网地址 💡 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击跳转到网站…...
【零基础入门Go语言】struct 和 interface:Go语言是如何实现继承的?
提到面向对象编程中的继承,许多人脑海中可能会浮现出 Java、C 等语言中那一套熟悉的类继承体系。然而,Go 语言作为一门别具一格的编程语言,并没有遵循传统的继承模式。那么,在 Go 语言的世界里,它是怎样实现类似于继承…...
麦田物语学习笔记:实现拖拽物品交换数据和在地图上生成物品
基本流程 1.代码思路 (1)InventoryUI的PlayerSlots与PlayerBag里一一对应,所以想要实现交换数据实际上是,先拿到被拖拽的物体所对的Slot的序号和目标的Slot序号,然后将这两个序号对调一下 (2)物品交换的数据逻辑应该在InventoryManager里去调用,因为InventoryManager里管理了p…...
一些计算机零碎知识随写(25年1月)-1
我原以为世界上有技术的那批人不会那么闲,我错了,被脚本真实了。 今天正隔着画画呢,手机突然弹出几条安全告警通知。 急忙打开服务器,发现问题不简单,直接关服务器重装系统..... 首先,不要认为小网站&…...
Qt学习笔记第81到90讲
第81讲 串口调试助手实现自动发送 为这个名叫“定时发送”的QCheckBox编写槽函数。 想要做出定时发送的效果,必须引入QT框架下的毫秒级定时器QTimer,查阅手册了解详情。 在widget.h内添加新的私有成员变量: QTimer *timer; 在widget类的构造…...
Centos9 + Docker 安装 MySQL8.4.0 + 定时备份数据库到本地
Centos9 Docker 安装 MySQL8.4.0 定时备份数据库到本地 创建目录,创建配置文件启动容器命令定时备份MySQL执行脚本Linux每日定时任务命令文件内参数其他时间参数 AT一次性定时任务 创建目录,创建配置文件 $ mkdir -p /opt/mysql/conf$ vim /opt/mysql/…...
网络原理一>UDP协议详解
UDP和TCP都是应用层中的重要协议,如果做基础架构开发,会用得多一些。 这一篇我们先简单聊一下的UDP TCP格式呈现: 我们知道UDP是一种无连接,面向数据报,全双工,不可靠传输特性的网络协议。 基本格式如图…...
MySQL的小问题
编码问题 不管官方使用什么编码:latin1、gbk、utf8、utfmb4。统一使用utfmb4 MySQL中的utf8并不是utf-8,它省略了一个字节,只是用三个字节存储所有的符号,utfmb4才是utf-8 远程登录问题: MySQL官方默认没有启动远程…...
Mac——Docker desktop安装与使用教程
摘要 本文是一篇关于Mac系统下Docker Desktop安装与使用教程的博文。首先介绍连接WiFi网络,然后详细阐述了如何在Mac上安装Docker,包括下载地址以及不同芯片版本的选择。接着讲解了如何下载基础镜像和指定版本镜像,旨在帮助用户在Mac上高效使…...
FastApi Swagger 序列化问题
问题 错误现象: fastapi的 swagger 界面无法正常打开控制台报错:raise PydanticInvalidForJsonSchema(fCannot generate a JsonSchema for {error_info}) 详细报错: File "d:\Envs\miniconda3\envs\xdagent\lib\site-packages\pydan…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...








