HarmonyOS:@LocalBuilder装饰器: 维持组件父子关系

news2025/1/13 11:10:46

一、前言

当开发者使用@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';

  @LocalBuilder
  citeLocalBuilder(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';

  @LocalBuilder
  citeLocalBuilder($$: 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
  // @Builder
  componentBuilder($$: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';

  @LocalBuilder
  citeLocalBuilder(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 };

  @LocalBuilder
  privateBuilder() {
    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)
    }
  }

  @LocalBuilder
  privateBuilderSecond() {
    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() // 调用局部@Builder
      Line()
        .width('100%')
        .height(10)
        .backgroundColor('#000000').margin(10)
      Text(`info2: ${this.info2.name}  ${this.info2.age}`) // Text1
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      this.privateBuilderSecond() // 调用局部@Builder
      Line()
        .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会刷新,原因是有装饰器修饰,可以监听到值的改变。
        })
    }
  }
}

效果图

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pytorch导出onnx模型并在C++环境中调用(含python和C++工程)

Pytorch导出onnx模型并在C环境中调用(含python和C工程) 工程下载链接:Pytorch导出onnx模型并在C环境中调用(python和C工程) 机器学习多层感知机MLP的Pytorch实现-以表格数据为例-含数据集和PyCharm工程中简单介绍了在…

git打补丁

1、应用场景 跨仓库升级 开发项目B使用的是开源项目A。开源项目A发现漏洞,作者进行了修复,我们可以通过使用git补丁的方式,将作者修改的内容复制到我 们的项目B中。 2、TortoiseGit方式 源仓库 格式化补丁 根据提交数量,生成…

计算机网络 (34)可靠传输的工作原理

前言 计算机网络可靠传输的工作原理主要依赖于一系列协议和机制,以确保数据在传输过程中能够准确无误地到达目的地。 一、基本概念 可靠传输指的是数据链路层的发送端发送什么,在接收端就收到什么,即保证数据的完整性、正确性和顺序性。由于网…

基于ADAS 与关键点特征金字塔网络融合的3D LiDAR目标检测原理与算法实现

一、概述 3D LiDAR目标检测是一种在三维空间中识别和定位感兴趣目标的技术。在自动驾驶系统和先进的空间分析中,目标检测方法的不断演进至关重要。3D LiDAR目标检测作为一种变革性的技术,在环境感知方面提供了前所未有的准确性和深度信息. 在这里&…

Vue3初学之常用的指令

v-bind:动态绑定属性 v-bind 用于动态绑定一个或多个属性,或一个组件 prop 到表达式的值。 v-model:双向数据绑定 见上篇 https://editor.csdn.net/md/?articleId145022994 v-if、v-else-if、v-else:条件渲染 v-show&…

docker中jenkins流水线式部署GitLab中springboot项目

本质就是将java项目拉取下来,并自动打包成docker镜像,运行 首先启动一个docker的jenkins 如果没有镜像使用我的镜像 通过网盘分享的文件:jenkins.tar 链接: https://pan.baidu.com/s/1VJOMf6RSIQbvW_V1zFD7eQ?pwd6666 提取码: 6666 放入服…

在ubuntu下对NFS做性能测试

安装NFS 首先,安装服务 sudo apt update sudo apt install nfs-kernel-server然后创建共享文件夹 # 请自定义你自己的共享目录 sudo mkdir -p /exports/nfs4/homes sudo chmod -R 777 /exports/nfs4/homes# 这个可以根据no_root_squash标致选择设置。 # 如果不设…

Open FPV VTX开源之默认MAVLink设置

Open FPV VTX开源之默认MAVLink设置 1. 源由2. 准备3. 连接4. 安装5. 配置6. 测试6.1 启动wfb-ng服务6.2 启动wfb-ng监测6.3 启动QGroundControl6.4 观察测试结果 7. 总结8. 参考资料9. 补充9.1 telemetry_tx异常9.2 DEBUG串口部分乱码9.3 PixelPilot软件问题 1. 源由 飞控图传…

26个开源Agent开发框架调研总结(2)

根据Markets & Markets的预测,到2030年,AI Agent的市场规模将从2024年的50亿美元激增至470亿美元,年均复合增长率为44.8%。 Gartner预计到2028年,至少15%的日常工作决策将由AI Agent自主完成,AI Agent在企业应用中…

mark 一下conductor github

Netflix 关闭conductor 后,后续https://orkes.io/content/ 继续在维护,github地址如下 https://github.com/conductor-oss/conductor 最新release为3.21.11

PyCharm文档管理

背景:使用PyCharmgit做文档管理 需求:需要PyCharm自动识别docx/xslx/vsdx等文件类型,并在PyCharm内点击文档时唤起系统内关联应用(如word、excel、visio) 设置步骤: 1、file -》 settings -》file types 2、在Files opened i…

嘉立创画原理图和PCB

一、环境 进入立创EDA官网 注册登录的环节就不介绍了。 登录账号后,选择专业版 二、原理图 工程中,有原理图和PCB,这里选择原理图 那么接下来就是进行绘制 元器件在如下区域搜索使用。 双击进行放置,也可以左键提前预览。 网…

科创驱动 | 华望系统科技荣膺西湖区年度前沿创新新锐企业

2025年1月3日,由中共西湖区党委、西湖区人民政府主办的“新年第一会”—西湖区科技创新大会在杭州隆重举行。大会现场揭晓了西湖区年度科技创新团队与项目,并发布了“2024西湖区科技十大事件”与“西湖区五大年度科技榜单”。杭州华望系统科技有限公司榜…

Monorepo设置:新手指南

Monorepo是一种项目代码管理方法,指在单个代码仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署的复杂性,并提供更好的可重用性和协作性。 简单理解:所有项目都在一个代码仓库中 📦,但这并不意…

[Python学习日记-75] 计算机基础与网络

[Python学习日记-75] 计算机基础与网络 简介 计算机基础 什么是网络编程 计算机网络 简介 本篇主要介绍的计算机基础是浓缩的,这是因为我们主要学习的是 Python,而 Python 主要是为了开发应用程序的,并不会用它来开发操作系统和嵌入式程序…

1. Doris分布式环境搭建

一. 环境准备 本次测试集群采用3台机器hadoop1、hadoop2、hadoop3, Frontend和Backend部署在同一台机器上,Frontend部署3台组成高可用,Backend部署3个节点,组成3副本存储。 主机IP操作系统FrontendBackendhadoop1192.168.47.128Centos7Foll…

【Java】-- 利用 jar 命令将配置文件添加到 jar 中

目录 1、准备 2、目标 3、步骤 3.1、安装 jdk 3.2、添加配置文件 3.3、校验 1、准备 java 环境hadoop-core-1.2.1.jar 和 core-site.xml 2、目标 将 core-site.xml 添加到 hadoop-core-1.2.1.jar 中。 3、步骤 3.1、安装 jdk 3.2、添加配置文件 jar -cvf hadoop-core-…

day14-Linux系统基础权限知识精讲

1. 给文件加特殊属性 1.1 chattr a:只能追加内容,不能删除 i:不能修改,不能删除;保护关键文件,防止非法写入 [rootoldboy ~]# chattr a test.txt [rootoldboy ~]# chattr i test.txt [rootoldboy ~]# echo 123 >> test.txt -bash: t…

Android使用系统消息与定时器实现霓虹灯效果

演示效果: 界面设计: 在帧布局FrameLayout中添加6个TextView 依次设置这6个TextView的宽,高,权重 也可在XML中直接设置 添加自定义颜色 关联自定义颜色到数组变量 关联6个TextView控件到数组变量 处理自定义系统消息 Handler _sysHandler new Han…

数据结构大作业——家谱管理系统(超详细!完整代码!)

目录 设计思路: 一、项目背景 二、功能分析 查询功能流程图: 管理功能流程图: 三、设计 四、实现 代码实现: 头文件 结构体 函数声明及定义 创建家谱树头结点 绘制家谱树(打印) 建立右兄弟…