用文字记录生活,留下美好瞬间
原创

自定义组件

共 7,432 字,需阅读 19 分钟
2023/12/29 上午
325 次阅读

自定义组件具有一下特点:

  • 可组合:允许开发者组合使用系统组件、及其属性和方法。
  • 可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用。
  • 数据驱动UI更新:通过状态变量的改变,来驱动UI的刷新。

#自定义组件的基本用法

以下示例展示了自定义组件的基本用法。

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
@Component struct HelloComponent { @State message: string = 'Hello, World!'; build() { // HelloComponent自定义组件组合系统组件Row和Text Row() { Text(this.message) .onClick(() => { // 状态变量message的改变驱动UI刷新,UI从'Hello, World!'刷新为'Hello, ArkUI!' this.message = 'Hello, ArkUI!'; }) } } }

如果在另外的文件中引用该自定义组件,需要使用 export 关键字导出,并在使用的页面 import 该自定义组件

HelloComponent可以在其他自定义组件中的build()函数中多次创建,实现自定义组件的重用。

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
@Entry @Component struct ParentComponent { build() { Column() { Text('ArkUI message') HelloComponent({ message: 'Hello World!' }); Divider() HelloComponent({ message: '你好,世界!' }); } } }

#自定义组件的基本结构

#struct

自定义组件基于struct实现,struct + 自定义组件名 + {...}的组合构成自定义组件,不能有继承关系。对于struct的实例化,可以省略new。

自定义组件名,类名,函数名不能和系统组件名相同。

#@component

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

          
  • 1
  • 2
  • 3
@Component struct MyComponent { }

#build()函数

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

          
  • 1
  • 2
  • 3
  • 4
  • 5
@Component struct MyComponent { build() { } }

#@Entry

@Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中,最多可以使用@Entry装饰一个自定义组件。@Entry可以接受一个可选的 LocalStorage 的参数。

          
  • 1
  • 2
  • 3
  • 4
@Entry @Component struct MyComponent { }
#EntryOptions
名称 类型 必填 说明
routeName string 表示作为命名路由页面的名字。
storage LocalStorage 页面级的UI状态存储。
useSharedStorage boolean 是否使用LocalStorage.getShared()接口返回的 LocalStorage 实例对象,默认值false。

当useSharedStorage 设置为 true,并且storage又被赋值时,useSharedStorge的值优先级更高。

          
  • 1
  • 2
  • 3
  • 4
@Entry({ routeName : 'myPage' }) @Component struct MyComponent { }

#@Reusable

@Reusable装饰的自定义组件具备可复用能力。

          
  • 1
  • 2
  • 3
  • 4
@Reusable @Component struct MyComponent { }

#成员函数/变量

自定义组件除了必须要实现build()函数外,还可以实现其他成员函数,成员函数具有以下约束:

  • 自定义组件的成员函数为私有的,且不建议声明成静态函数。

自定义组件可以包含成员变量,成员变量具有以下约束:

  • 自定义组件的成员变量为私有的,且不建议声明成静态变量。
  • 自定义组件的成员变量本地初始化有些是可选的,有些是必选的。具体是否需要本地初始化,是否需要从父组件通过参数传递初始化子组件的成员变量,请参考 状态管理

#自定义组件的参数规定

从上文的示例中,我们已经了解到,可以在build方法里创建自定义组件,在创建自定义组件的过程中,根据装饰器的规则来初始化自定义组件的参数。

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
@Component struct MyComponent { private countDownFrom: number = 0; private color: Color = Color.Blue; build() { } } @Entry @Component struct ParentComponent { private someColor: Color = Color.Pink; build() { Column() { // 创建MyComponent实例,并将创建MyComponent成员变量countDownFrom初始化为10,将成员变量color初始化为this.someColor MyComponent({ countDownFrom: 10, color: this.someColor }) } } }

下面的示例代码将父组件中的函数传递给子组件,并在子组件中调用。

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
@Entry @Component struct Parent { @State cnt: number = 0 submit: () => void = () => { this.cnt++; } build() { Column() { Text(`${this.cnt}`) Son({ submitArrow: this.submit }) } } } @Component struct Son { submitArrow?: () => void build() { Row() { Button('add') .width(80) .onClick(() => { if (this.submitArrow) { this.submitArrow() } }) } .height(56) } }

#build()函数

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

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

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

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
@Entry @Component struct MyComponent { build() { // 根节点唯一且必要,必须为容器组件 Row() { ChildComponent() } } } @Component struct ChildComponent { build() { // 根节点唯一且必要,可为非容器组件 Image('test.jpg') } }
  • 不允许声明本地变量,反例如下。
          
  • 1
  • 2
  • 3
  • 4
build() { // 反例:不允许声明本地变量 let num: number = 1; }
  • 不允许在UI描述里直接使用console.info,但允许在方法或者函数里使用,反例如下。
          
  • 1
  • 2
  • 3
  • 4
build() { // 反例:不允许console.info console.info('print debug log'); }
  • 不允许创建本地的作用域,反例如下。
          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
build() { // 反例:不允许本地作用域 { ... } }
  • 不允许调用没有用@Builder装饰的方法,允许系统组件的参数是TS方法的返回值。
          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
@Component struct ParentComponent { doSomeCalculations() { } calcTextValue(): string { return 'Hello World'; } @Builder doSomeRender() { Text(`Hello World`) } build() { Column() { // 反例:不能调用没有用@Builder装饰的方法 this.doSomeCalculations(); // 正例:可以调用 this.doSomeRender(); // 正例:参数可以为调用TS方法的返回值 Text(this.calcTextValue()) } } }
  • 不允许使用switch语法,如果需要使用条件判断,请使用 if 。示例如下。
          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
build() { Column() { // 反例:不允许使用switch语法 switch (expression) { case 1: Text('...') break; case 2: Image('...') break; default: Text('...') break; } // 正例:使用if if(expression == 1) { Text('...') } else if(expression == 2) { Image('...') } else { Text('...') } } }
  • 不允许使用表达式,请使用if组件,示例如下。
          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
build() { Column() { // 反例:不允许使用表达式 (this.aVar > 10) ? Text('...') : Image('...') // 正例:使用if判断 if(this.aVar > 10) { Text('...') } else { Image('...') } } }
          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
@Component struct MyComponent { @State textColor: Color = Color.Yellow; @State columnColor: Color = Color.Green; @State count: number = 1; build() { Column() { // 应避免直接在Text组件内改变count的值 Text(`${this.count++}`) .width(50) .height(50) .fontColor(this.textColor) .onClick(() => { this.columnColor = Color.Red; }) Button("change textColor").onClick(() =>{ this.textColor = Color.Pink; }) } .backgroundColor(this.columnColor) } }

#自定义组件通用样式

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
@Component struct ChildComponent { build() { Button(`Hello World`) } } @Entry @Component struct MyComponent { build() { Row() { ChildComponent() .width(200) .height(300) .backgroundColor(Color.Red) } } }

ArkUI给自定义组件设置样式时,相当于给ChildComponent套了一个不可见的容器组件,而这些样式是设置在容器组件上的,而非直接设置给ChildComponent的Button组件。通过渲染结果我们可以很清楚的看到,背景颜色红色并没有直接生效在Button上,而是生效在Button所处的开发者不可见的容器组件上。

自由转载 - 署名 - 非商业性使用
https://zhangwurui.cn/article/81
0/0条看法
访客身份
在下有一拙见,不知...
期待你的捷足先登