当前位置: 首页 > news >正文

【HarmonyOS】ArkUI - 自定义组件和结构重用

文章目录

  • 一、自定义组件
    • 1、使用场景
    • 2、基本结构
      • (1)struct
      • (2)@Component
      • (3)build()函数
      • (4)@Entry
    • 3、生命周期
      • (1)生命周期函数
      • (2)重新渲染
  • 二、组件扩展
    • 1、@Builder
      • (1)概念
      • (2)分类
      • (3)参数传递规则
      • (3)常见问题
    • 2、@LocalBuilder(维持组件关系)
      • (1)概念
      • (2)和局部@Builder使用区别
      • (3)参数传递规则
    • 3、@BuilderParam(引用@Builder)
      • (1)概念
      • (2)多个 @BuilderParam 参数
  • 三、结构重用
    • 1、`@Extend` 装饰器
      • 2、`@Styles` 装饰器
        • 关键点
      • 四、装饰器对比
      • 五、使用场景
      • 六、注意事项

一、自定义组件

在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在鸿蒙应用开发中,@Component 是声明自定义组件的核心装饰器,用于将一个类标记为可复用的 UI 组件。自定义组件是构建鸿蒙 UI 界面的基础,支持组合、嵌套和复用,极大提升了开发效率。

一个完整的自定义组件需包含以下部分:

  1. @Component 装饰器:标记类为自定义组件。
  2. build() 方法:定义组件的 UI 结构,必须实现。
  3. 成员变量:存储组件状态(如 @State 装饰的响应式数据)。
  4. 生命周期方法(可选):如 aboutToAppear()(组件即将显示)、aboutToDisappear()(组件即将销毁)。

1、使用场景

  1. 自定义组件 @Component struct HelloComponent 组合系统组件。
  2. 在其他自定义组件的build()函数中多次创建HelloComponent,以实现自定义组件的重用。
  3. 通过状态变量的改变,来驱动UI的刷新。

2、基本结构

(1)struct

自定义组件基于struct实现,struct + 自定义组件名 + {...}的组合构成自定义组件,不能有继承关系

(2)@Component

@Component装饰器仅能装饰struct关键字声明的数据结构。struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI,一个struct只能被一个@Component装饰

(3)build()函数

build()函数用于定义自定义组件的声明式UI描述,自定义组件必须定义build()函数

所有在build()函数中声明的语句统称为UI描述,UI描述需要遵循以下规则:

  • @Entry装饰的自定义组件,其build()函数下的根节点唯一且必要,且必须为容器组件,其中ForEach禁止作为根节点。
  • @Component装饰的自定义组件,其build()函数下的根节点唯一且必要,可以为非容器组件,其中ForEach禁止作为根节点。

(4)@Entry

@Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中,仅允许存在一个由@Entry装饰的自定义组件作为页面的入口。二、应用模型。

3、生命周期

(1)生命周期函数

  1. aboutToAppear?(): void

    aboutToAppear函数在创建自定义组件的新实例后,在执行其build()函数之前执行。允许在aboutToAppear函数中改变状态变量,更改将在后续执行build()函数中生效。

  2. onDidBuild?(): void

    onDidBuild函数在执行自定义组件的build()函数之后执行,开发者可以在这个阶段进行埋点数据上报等不影响实际UI的功能。不建议在onDidBuild函数中更改状态变量、使用animateTo等功能,这可能会导致不稳定的UI表现。

  1. onPageShow()(仅页面级组件)
    • 触发时机:组件所在页面被显示时(如从后台切换到前台)。
    • 作用:可以在这里执行页面显示后的逻辑(如刷新数据)。
  2. onPageHide()(仅页面级组件)
    • 触发时机:组件所在页面被隐藏时(如切换到其他页面)。
    • 作用:可以暂停耗时操作(如动画、定时器)。
  1. aboutToDisappear?(): void

    aboutToDisappear函数在自定义组件析构销毁时执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。

img

(2)重新渲染

当触发事件(比如点击)改变状态变量时,或者LocalStorage / AppStorage中的属性更改,并导致绑定的状态变量更改其值时:

  1. 框架观察到变化,启动重新渲染。
  2. 根据框架记录的状态变量和组件的映射关系,仅刷新发生变化的状态变量所关联的组件,实现最小化更新。

二、组件扩展

1、@Builder

@Builder装饰器装饰器专为构建模块化、可复用的UI结构而设计,其内部禁止定义状态变量和调用组件生命周期方法,仅支持通过参数与调用方进行数据交互。

(1)概念

  1. 作用

定义一个构建器函数,用于生成可复用的 UI 片段。类似于组件,但更轻量,无独立生命周期。

  1. 用法
    1. @Builder 装饰函数。
    2. 函数内部返回 UI 组件(如 ColumnText 等)。
    3. 可通过参数接收外部数据。
  2. 示例
@Builder
function MyButton({ text, onClick }: { text: string; onClick: () => void }) {Button(text).onClick(onClick).width('100%').backgroundColor('#007DFF').fontColor('#FFFFFF')
}@Entry
@Component
struct MainPage {build() {Column() {MyButton({ text: "点击我", onClick: () => console.log("按钮被点击") })}}
}

(2)分类

  1. 私有自定义构建函数(@component组件内部)
@BuildershowTextBuilder() {// @Builder装饰此函数,使其能以链式调用的方式配置并构建Text组件Text('Hello World').fontSize(30).fontWeight(FontWeight.Bold)}
  • 在自定义组件中,this指代当前所属组件,组件的状态变量可在自定义构建函数内访问。建议通过this访问组件的状态变量,而不是通过参数传递。
  1. 全局自定义构建函数(@component组件外部)
@Builder
function showTextBuilder() {Text('Hello World').fontSize(30).fontWeight(FontWeight.Bold)
}
  • 如果不涉及组件状态变化,建议使用全局的自定义构建函数。
  • 全局自定义构建函数允许在build函数和其他自定义构建函数中调用。

(3)参数传递规则

  1. 按值传递参数

调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder函数内的UI刷新。

@State label: string = 'Hello';
Column() {overBuilder(this.label)}
  1. 按引用传递参数

按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder函数内的UI刷新。

@State label: string = 'Hello';
Column() {overBuilder({ paramA1: this.label })Button('Click me').onClick(() => {this.label = 'ArkUI';})}

(3)常见问题

  1. @Builder存在两个或者两个以上参数,即使通过对象字面量形式传递,值的改变也不会触发UI刷新。因此,@Builder只接受一个参数,值的改变会引起UI的刷新。
  2. 在parentBuilder函数中创建自定义组件HelloComponent,传递参数为class对象并修改对象内的值时,UI不会触发刷新功能。传递参数为对象字面量形式并修改对象内的值时,UI触发刷新功能。

2、@LocalBuilder(维持组件关系)

(1)概念

  1. 作用

定义一个局部构建器,仅在当前组件内部可见。类似于私有方法,用于封装组件内部的 UI 逻辑。

  1. 用法
    1. 在组件类内部使用 @LocalBuilder 装饰方法。
    2. 方法返回 UI 组件,只能在当前组件的 build() 中调用。
  2. 示例
@Component
struct MyComponent {@LocalBuilderprivate buildHeader() {Text('头部标题').fontSize(24).fontWeight(FontWeight.Bold)}build() {Column() {this.buildHeader() // 仅在当前组件内可用Text('主体内容')}}
}

(2)和局部@Builder使用区别

img

@Builder componentBuilder()通过this.componentBuilder的形式传给子组件@BuilderParam customBuilderParam,this指向子组件Child的实例。

@LocalBuilder componentBuilder()通过this.componentBuilder的形式传给子组件@BuilderParam customBuilderParam,this指向父组件Parent的实例。

(3)参数传递规则

  • 传入一个对象字面量参数时按引用传递,其他方式按值传递。
  1. 按引用传递参数
    1. 按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@LocalBuilder函数内的UI刷新。

当子组件引用父组件的@LocalBuilder函数并传入状态变量时,状态变量的改变不会触发@LocalBuilder函数内的UI刷新。这是因为调用@LocalBuilder装饰的函数创建出来的组件绑定于父组件,而状态变量的刷新机制仅作用于当前组件及其子组件,对父组件无效。而使用@Builder修饰函数可触发UI刷新,原因在于@Builder改变了函数的this指向,使创建出来的组件绑定到子组件上,从而在子组件修改变量能够实现@Builder中的UI刷新。

组件Child将状态变量传递到Parent的@Builder和@LocalBuilder函数内。在@Builder函数内,this指向Child,参数变化能触发UI刷新。在@LocalBuilder函数内,this指向Parent,参数变化不会触发UI刷新。若@LocalBuilder函数内引用Parent的状态变量发生变化,UI能正常刷新。

  1. 按值传递参数

调用@LocalBuilder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@LocalBuilder函数内的UI刷新。

3、@BuilderParam(引用@Builder)

(1)概念

  1. 作用

@Builder 函数中声明可被外部覆盖的参数,类似于组件的 @Prop,但仅用于构建器。

  1. 用法

    1. @Builder 函数内部使用 @BuilderParam 装饰变量。
    2. 可提供默认值,外部可通过参数覆盖。
  2. 示例

@Component
struct son {// 1.定义 BuilderParam 接受外部传入的 UI,并设置默认值// 如果没有尾随闭包,则使用默认@BuilderParam contentBuilder: () => void = this.defaultBuilder// 默认的 Builder@BuilderdefaultBuilder() {Text('awfawsgfwagf').backgroundColor(Color.Pink).width('100%')}build() {Column() {// 2.使用 @BuilderParam 装饰的成员变量this.contentBuilder()}}
}@Entry
@Component
struct Index {build() {Column() {son() {// 直接传递进来给到 BuilderParam(尾随闭包 -》即跟在 son 后面的花括号和里面的内容)Button('sdfvadfvasf')}}}
}

(2)多个 @BuilderParam 参数

子组件拥有多个 BuilderParam,必须通过参数的方式传入

@Component
struct Child {label: string = 'Child';@BuildercustomBuilder() {}@BuildercustomChangeThisBuilder() {}@BuilderParam customBuilderParam: () => void = this.customBuilder;@BuilderParam customChangeThisBuilderParam: () => void = this.customChangeThisBuilder;build() {Column() {this.customBuilderParam()this.customChangeThisBuilderParam()}}
}@Entry
@Component
struct Parent {label: string = 'Parent';@BuildercomponentBuilder() {Text(`${this.label}`)}build() {Column() {// 调用this.componentBuilder()时,this指向当前@Entry所装饰的Parent组件,即label变量的值为"Parent"。this.componentBuilder()Child({// 把this.componentBuilder传给子组件Child的@BuilderParam customBuilderParam,// this指向的是子组件Child,即label变量的值为"Child"。customBuilderParam: this.componentBuilder,// 把():void=>{this.componentBuilder()}传给子组件Child的@BuilderParam customChangeThisBuilderParam,// 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent"。customChangeThisBuilderParam: (): void => {this.componentBuilder()}})}}
}

三、结构重用

1、@Extend 装饰器

  1. 作用

扩展现有组件的样式或事件,创建自定义变体,而无需重写整个组件。

  • @Extend支持封装指定组件的私有属性、私有事件和自身定义的全局方法,
  • @Extend装饰的方法支持参数。
  • @Extend的参数可以为状态变量,当状态变量改变时,UI可以正常的被刷新渲染。
  • @Extend仅支持在全局定义,不支持在组件内部定义。
  1. 用法

  2. @Extend 装饰类,继承自目标组件类。

  3. 在类中使用 override 关键字重写样式或事件方法。

  4. 示例:扩展 Button 组件

@Extend(Button)
class MyButton {// 重写默认样式override width: string = '100%'override backgroundColor: ResourceStr = '#007DFF'override fontColor: ResourceStr = '#FFFFFF'override fontSize: number = 18// 添加自定义事件@Link private count: number = 0constructor(count: Link<number>) {this.count = count}// 扩展点击事件override build() {super.build().onClick(() => {this.count++console.log('按钮被点击,计数:', this.count)})}
}@Entry
@Component
struct MainPage {@State count: number = 0build() {Column() {// 使用扩展后的按钮MyButton(this.$count) {Text(`点击 ${this.count}`)}}}
}
  1. 关键点
  • 继承特性:保留原组件的所有属性和方法。
  • 样式覆盖:通过 override 直接修改属性默认值。

2、@Styles 装饰器

  1. 作用

@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。通过@Styles装饰器可以快速定义并复用自定义样式。

  • @Styles方法不能有参数,编译期会报错,表明@Styles方法不支持参数。
  • 不支持在@Styles方法内使用逻辑组件,逻辑组件内的属性不生效。
  1. 用法

    1. 定义在组件内的@Styles可以通过this访问组件的常量和状态变量,并可以在@Styles里通过事件来改变状态变量的值。

    2. 在组件中通过 @Styles 引用该接口,并应用样式。

  2. 示例

@Entry
@Component
struct FancyUse {@State heightValue: number = 50;@Stylesfancy() {.height(this.heightValue).backgroundColor(Color.Blue).onClick(() => {this.heightValue = 100;})}build() {Column() {Button('change height').fancy()}.height('100%').width('100%')}
}
关键点
  • 样式复用:通过 apply() 方法将样式应用到组件。
  • 类型安全:样式接口确保属性类型匹配。
  • 灵活组合:可组合多个样式(如 .apply(style1).apply(style2))。

四、装饰器对比

装饰器核心用途应用对象生命周期复用级别
@Extend扩展现有组件的样式和功能组件类组件级
@Styles抽取和复用通用样式和事件配置接口 / 对象样式级
@Builder创建可复用的 UI 构建函数函数UI 片段级

五、使用场景

  1. @Extend
    • 自定义系统组件(如 ButtonText)。
    • 需要保留原组件特性并添加额外功能。
  2. @Styles
    • 统一应用风格(如按钮、卡片的通用样式)。
    • 多组件共享相同样式配置。
  3. @Builder
    • 封装复杂 UI 结构(如列表项、表单)。
    • 实现 UI 组件的参数化配置。

六、注意事项

  1. 性能考量
    • @Builder 函数在每次渲染时执行,避免在其中进行复杂计算。
    • @Extend 适用于少量定制,过度扩展可能导致代码冗余。
  2. 类型约束
    • @Styles 接口需确保属性类型与目标组件匹配。
    • @Builder 参数需明确定义类型,提高代码可读性。
  3. 组合使用
    • 可组合使用这些装饰器(如在 @Builder 中应用 @Styles)。
http://www.lryc.cn/news/593855.html

相关文章:

  • 基于FPGA的多级流水线加法器verilog实现,包含testbench测试文件
  • Python基础-列表
  • Python趣味算法:借书方案知多少 | 排列组合穷举法详解
  • 06 51单片机之矩阵键盘
  • Laravel 框架NOAUTH Authentication required 错误解决方案-优雅草卓伊凡
  • Autosar RTE实现观测量生成-基于ETAS软件
  • MYSQL:从增删改查到高级查询
  • 技术演进中的开发沉思-40 MFC系列:多线程协作
  • [特殊字符] 小程序 vs 智能体:下一代应用开发,谁主沉浮?
  • 社交圈子系统开源社交源码 / 小程序+H5+APP 多端互通的底层技术分析
  • 分享如何在保证画质的前提下缩小视频体积实用方案
  • 敏捷开发的历史演进:从先驱实践到全域敏捷(1950s-2025)
  • Hiredis 构建 Redis 命令实战指南
  • 音视频学习(四十一):H264帧内压缩技术
  • 【AI】文生图文生视频
  • 吴恩达机器学习笔记(3)—线性代数回顾(可选)
  • 17.TaskExecutor与ResourceManager交互
  • 微服务雪崩防护最佳实践之sentinel
  • ThinkSound:阿里开源首个“会思考”的音频生成模型——从“看图配音”到“听懂画面”的技术跃迁
  • SpringBoot 整合 Langchain4j 实现会话记忆存储深度解析
  • Node.js 与 Java 性能对比
  • 【Kafka】深入理解 Kafka MirrorMaker2 - 实战篇
  • Node.js v20.19.4 (LTS)升级
  • Python模块和包
  • 【PTA数据结构 | C语言版】邻接矩阵表示的图基本操作
  • simulink系列之模型接口表生成及自动连线脚本
  • LeetCode|Day19|14. 最长公共前缀|Python刷题笔记
  • CSS篇——第一章 六十五项关键技能(上篇)
  • Python高级数据类型:集合(Set)
  • 【通识】PCB文件