鸿蒙:自定义组件
@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!';
})
}
}
}
class HelloComponentParam {
message: string = ""
}
@Entry
@Component
struct ParentComponent {
param: HelloComponentParam = {
message: 'Hello, World!'
}
build() {
Column() {
Text('ArkUI message')
HelloComponent(this.param);
Divider()
HelloComponent(this.param);
}
}
}
1.自定义组件的基本结构
- @Entry:@Entry装饰的自定义组件将作为UI页面的入口。
- struct:自定义组件基于struct实现,struct + 自定义组件名 + {...}的组合构成自定义组件,不能有继承关系。对于struct的实例化,可以省略new。
- @Component:@Component装饰器仅能装饰struct关键字声明的数据结构,struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI
- build()函数:build()函数用于定义自定义组件的声明式UI描述,自定义组件必须定义build()函数。
- @Reusable:@Reusable装饰的自定义组件具备可复用能力
@Entry({ routeName : 'myPage' })
@Reusable
@Component
struct MyComponent {
build() {
}
}
2.成员函数/变量
自定义组件的成员函数为私有的,且不建议声明成静态函数。
下面的示例代码将父组件中的函数传递给子组件,并在子组件中调用。
@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()
}
})
}
.justifyContent(FlexAlign.SpaceBetween)
.height(56)
}
}
3.build()函数
所有声明在build()函数的语句,我们统称为UI描述,UI描述需要遵循以下规则:
- @Entry装饰的自定义组件,其build()函数下的根节点唯一且必要,且必须为容器组件,其中ForEach禁止作为根节点。
- @Component装饰的自定义组件,其build()函数下的根节点唯一且必要,可以为非容器组件,其中ForEach禁止作为根节点。
@Entry
@Component
struct MyComponent {
build() {
// 根节点唯一且必要,必须为容器组件
Row() {
ChildComponent()
}
}
}
@Component
struct ChildComponent {
build() {
// 根节点唯一且必要,可为非容器组件
Image('test.jpg')
}
}
- 不允许声明本地变量,反例如下。
- 不允许在UI描述里直接使用console.info,但允许在方法或者函数里使用,反例如下
- 不允许创建本地的作用域,反例如下。
- 不允许调用没有用@Builder装饰的方法,允许系统组件的参数是TS方法的返回值。
- 不允许使用switch语法,如果需要使用条件判断,请使用if。示例如下。
- 不允许使用表达式,反例如下。
- 不允许直接改变状态变量,会造成循环渲染的风险。
build() {
// 反例1:不允许声明本地变量
let a: number = 1;
// 反例2:不允许console.info
console.info('print debug log');
// 反例3:不允许本地作用域
{
...
}
}
@Component
struct ParentComponent {
@State count: number = 1;
doSomeCalculations() {
}
calcTextValue(): string {
return 'Hello World';
}
@Builder doSomeRender() {
Text(`Hello World`)
}
build() {
Column() {
// 反例4:不能调用没有用@Builder装饰的方法
this.doSomeCalculations();
// 正例:可以调用
this.doSomeRender();
// 正例:参数可以为调用TS方法的返回值
Text(this.calcTextValue())
// 反例5:不允许使用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('...')
}
// 反例6:不允许使用表达式
(this.aVar > 10) ? Text('...') : Image('...')
//反例7: 应避免直接在Text组件内改变count的值
Text(`${this.count++}`)
}
// 反例 8:
@State arr : Array<...> = [ ... ];
ForEach(this.arr.sort().filter(...),
item => {
...
})
// 正确的执行方式为:filter返回一个新数组,后面的sort方法才不会改变原数组this.arr
ForEach(this.arr.filter(...).sort(),
item => {
...
})
}
4.自定义组件通用样式
- 自定义组件通过“.”链式调用的形式设置通用样式。一行一个
@Component
struct MyComponent2 {
build() {
Button(`Hello World`)
}
}
@Entry
@Component
struct MyComponent {
build() {
Row() {
MyComponent2()
.width(200)
.height(300)
.backgroundColor(Color.Red)
}
}
}