全网最详细的鸿蒙UI范式基本语法!

4.UI 范式基本语法

1.概述

在初步了解了ArkTS语言之后,我们以一个具体的示例来说明ArkTS的基本组成。如下图所示,当开发者点击按钮时,文本内容从“Hello World”变为“Hello ArkUI”。

图1 示例效果图

本示例中,ArkTS的基本组成如下所示。

图2 ArkTS的基本组成

说明

自定义变量不能与基础通用属性/事件名重复

  • 装饰器: 用于装饰类、结构、方法以及变量,并赋予其特殊的含义。如上述示例中@Entry、@Component和@State都是装饰器,@Component表示自定义组件,@Entry表示该自定义组件为入口组件,@State表示组件中的状态变量,状态变量变化会触发UI刷新。
  • UI描述:以声明式的方式来描述UI的结构,例如build()方法中的代码块。
  • 自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Hello。
  • 系统组件:ArkUI框架中默认内置的基础和容器组件,可直接被开发者调用,比如示例中的Column、Text、Divider、Button。
  • 属性方法:组件可以通过链式调用配置多项属性,如fontSize()、width()、height()、backgroundColor()等。
  • 事件方法:组件可以通过链式调用设置多个事件的响应逻辑,如跟随在Button后面的onClick()。
  • 系统组件、属性方法、事件方法具体使用可参考基于ArkTS的声明式开发范式

除此之外,ArkTS扩展了多种语法范式来使开发更加便捷:

  • @Builder/@BuilderParam:特殊的封装UI描述的方法,细粒度的封装和复用UI描述。
  • @Extend/@Styles:扩展内置组件和封装属性样式,更灵活地组合内置组件。
  • stateStyles:多态样式,可以依据组件的内部状态的不同,设置不同样式。

2.声明式 UI描述

ArkTS以声明方式组合和扩展组件来描述应用程序的UI,同时还提供了基本的属性、事件和子组件配置方法,帮助开发者实现应用交互逻辑。

1. 创建组件

根据组件构造方法的不同,创建组件包含有参数和无参数两种方式。

说明

创建组件时不需要new运算符。

无参数

如果组件的接口定义没有包含必选构造参数,则组件后面的“()”不需要配置任何内容。例如,Divider组件不包含构造参数。

Column() {
  Text('item 1')
  Divider()
  Text('item 2')
}

有参数

如果组件的接口定义包含构造参数,则在组件后面的“()”需要配置相应参数。

Image('https://xyz/test.jpg')
// string类型的参数
Text('test')
// $r形式引入应用资源,可应用于多语言场景
Text($r('app.string.title_value'))
// 无参数形式
Text()
  • 变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求。
Image(this.imagePath)
Image('https://' + this.imageUrl)
Text(`count: ${this.count}`)
2. 配置属性

属性方法以“.”链式调用的方式配置系统组件的样式和其他属性,建议每个属性方法单独写一行。

Text('test')
  .fontSize(12)
Image('test.jpg')
  .alt('error.jpg')    
  .width(100)    
  .height(100)
Text('hello')
  .fontSize(this.size)
Image('test.jpg')
  .width(this.count % 2 === 0 ? 100 : 200)    
  .height(this.offset + 100)
  • 对于系统组件,ArkUI还为其属性预定义了一些枚举类型供开发者调用,枚举类型可以作为参数传递,但必须满足参数类型要求。
Text('hello')
  .fontSize(20)
  .fontColor(Color.Red)
  .fontWeight(FontWeight.Bold)
3. 配置事件

事件方法以“.”链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行。

Button('Click me')
  .onClick(() => {
    this.myText = 'ArkUI';
  })

使用箭头函数表达式配置组件的事件方法,要求使用“() => {...}”,以确保函数与组件绑定,同时符合ArkTS语法规范。

Button('add counter')
  .onClick(() => {
    this.counter += 2;
  })

使用组件的成员函数配置组件的事件方法,需要bind this。ArkTS语法不推荐使用成员函数配合bind this去配置组件的事件方法。

myClickHandler(): void {
  this.counter += 2;
}
...
Button('add counter')
  .onClick(this.myClickHandler.bind(this))
fn = () => {
  console.info(`counter: ${this.counter}`)
  this.counter++
}
  ...
  Button('add counter')
  .onClick(this.fn)

说明

箭头函数内部的this是词法作用域,由上下文确定。匿名函数可能会有this指向不明确问题,在ArkTS中不允许使用。

4. 配置子组件

如果组件支持子组件配置,则需在尾随闭包"{...}"中为组件添加子组件的UI描述。Column、Row、Stack、Grid、List等组件都是容器组件。

Column() {
  Text('Hello')
    .fontSize(100)
  Divider()
  Text(this.myText)
    .fontSize(100)
    .fontColor(Color.Red)
}
Column() {
  Row() {
    Image('test1.jpg')
      .width(100)
      .height(100)
    Button('click +1')
      .onClick(() => {
        console.info('+1 clicked!');
      })
  }
}

3.样式和结构重用

随着页面复杂程度提高,页面中会有很多的样式&结构代码,其中难免重复的部分,如果可以提取出来重复使用,就可以提升编码效率,减少重复代码,提升代码可读性。

咱们来来学习3 种样式&结构复用的语法:

  1. @Styles: 抽取公共样式、事件
  2. @Extends:扩展组件样式、事件
  3. @Builder:轻量级的元素复用机制(结构、样式、事件)- 重点掌握
1.@Styles

示例

不同于@Extend 用来给某个组件扩展,@Styles 可以抽取 通用的事件和属性供所有组件使用

注意:

  1. @Styles 不支持传递参数
  2. 可以继续添加样式覆盖 @Styles 中定义的内容
  3. 组件内部写法,可以通过 this 访问组件属性
2.@Extend

通过 @Extend 可以扩展组件的样式、事件,实现复用效果

基本语法:.ets组件的最上方编写代码

注意:

① 扩展的组件和使用的组件同名,比如 @Extend(Text) ,那么后续通过 Text 才可以点出该扩展

② function 的名字不能与组件本身拥有的属性同名,自身属性优先级更高

例如:Text组件上本身自带有一个fontStyle属性,我们就不能再@Extend(Text) function fontStyle()一个同名方法了

③ 只能在当前组件有用,不能跨组件使用

3.@Builder

如果连结构也需要抽取,就可以使用@Builder,他也可以叫做 自定义构建函数

// ---------------------------------全局Builder
@Builder
function GlobalTextItem(title: string) {
  Text(title)
    .fontSize(30)
    .onClick(() => {
      // 逻辑略
    })
}

@Entry
@Component
struct Day01_04_Builder {
  @State message: string = '@Builder';

  build() {
    Column({ space: 20 }) {
      // 使用全局 Builder
      GlobalTextItem('你好')
      
      // 使用本地 Builder
      this.LocalTextItem('西兰花炒蛋')
    }
    .width('100%')
    .height('100%')
  }
// ----------------------组件内部builder
  @Builder
  LocalTextItem(title: string) {
    Text(title)
      .fontSize(30)
      .onClick(() => {
        // 逻辑略
      })
  }
}

总结

名称

适合

参数

@Styles

抽取 公共样式、事件

不可以传递参数

@Extend

抽取 特定组件 样式、事件

可以传递参数

@Builder(重点掌握)

抽取 结构、样式、事件

可以传递参数

4.自定义组件

1.概述

在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。

  • struct:自定义组件基于struct实现,struct + 自定义组件名 + {...}的组合构成自定义组件,不能有继承关系。对于struct的实例化,可以省略new。
  • @Component:@Component装饰器仅能装饰struct关键字声明的数据结构。
  • build()函数:build()函数用于定义自定义组件的声明式UI描述,自定义组件必须定义build()函数。
  • @Entry:@Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中,最多可以使用@Entry装饰一个自定义组件。
  • @Preview:如果想要单独预览组件,可以使用@Preview 进行装饰

注意:自定义组件必须导出后,才能在其他组件中导入使用

export struct SonCom {}

父传子

子传父

2.页面路由(router模式)
1.概述

页面路由指的是在应用程序中实现不同页面之间的跳转,以及数据传递。

我们先明确自定义组件和页面的关系:

  • 自定义组件:@Component 装饰的UI单元,
  • 页面:即应用的UI页面。可以由一个或者多个自定义组件组成。
    • @Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry

通过 Router 模块就可以实现这个功能. import { router } from '@kit.ArkUI'

步骤:

  1. 创建页面 -> 页面与组件不同的地方是有且只有一个入口组件( @Entry修饰),并且在src/main/resources/base/profile/main_pages.json有配置好了页面路径
    1. 口诀:一入口,一配置

  1. router控制页面跳转

  1. 带参数跳转并获取

2.pushUrl 与 replaceUrl

先来看看 pushUrl的情况

  1. 默认打开首页 → 首页入栈
  2. pushUrl 去详情页 → 详情页入栈
  3. back 返回上一页 → 详情页出栈
  4. 此时页面栈中应该只有一个页面

整一个过程中,都可以 router.getLength 进行查看

再来看看replaceUrl的情况

  1. 默认打开首页 → 首页入栈
  2. replaceUrl 去详情页 → 详情页替换首页,首页销毁
  3. back 无法返回 → 没有上一页

跳转到登录页面时可以使用 replaceUrl,因为无需在页面栈中保存其他页面的页面栈信息了。

页面栈相关 api

  // 获取页面栈长度
    router.getLength()

    // 获取页面状态
    let page = router.getState();
    console.log('页面栈数量:' + page.index);
    console.log('跳转的路由:' + page.name);
    console.log('路由路径:' + page.path);
    // 清空页面栈
    // router.clear()
3.路由模式

路由提供了两种不同的跳转模式:

  1. standard(标准实例模式)
  2. Single(单实例模式)

不同模式的决定了页面是否会创建多个实例:

  1. Standard:无论之前是否添加过,一直添加到页面栈【默认】
    1. 场景:适用于每次跳转都呈现全新内容或状态的场景,避免数据展示紊乱(数据变化
  1. Single:如果之前加过页面,会使用之前添加的页面【需要添加参数手动修改】
    1. 场景:适用于那些需要保留页面状态或避免重复创建相同页面的场景(数据不变化

路由模式语法:

总结

路由跳转的方式有pushUrl和replaceUrl

pushUrl的跳转要考虑两种模式:标准模式,单例模式

  1. pushUrl({},模式为标准模式(默认的模式)) -> 页面栈中的表现形式

  1. pushUrl({},模式为单例模式) -> 页面栈中的表现形式

  1. replaceUrl() -> 跳转以后的前一个页面自动销毁了

5.@BuilderParam装饰器:引用@Builder函数

1.概述

当开发者创建了自定义组件,并想对该组件添加特定功能,例如想在某一个指定的自定义组件中添加一个点击跳转操作,此时若直接在组件内嵌入事件方法,将会导致所有该自定义组件的实例都增加了功能。为解决此问题,ArkUI引入了@BuilderParam装饰器,@BuilderParam用来装饰指向@Builder方法的变量(@BuilderParam是用来承接@Builder函数的)。开发者可以在初始化自定义组件时,使用不同的方式(如:参数修改、尾随闭包、借用箭头函数等)对@BuilderParam装饰的自定义构建函数进行传参赋值,在自定义组件内部通过调用@BuilderParam为组件增加特定的功能。该装饰器用于声明任意UI描述的一个元素,类似slot占位符。

简单来说就是父组件既可以传值/函数也可以传 UI 组件

2.用法

1.单个@BuilderParam

使用尾随闭包的方式传入:

  • 组件内有且仅有一个使用 @BuilderParam 装饰的属性,即可使用尾随闭包
  • 内容直接在 {} 传入即可

注意:

  • 此场景下自定义组件不支持使用通用属性。

2.多个@BuilderParam

  1. 自定义组件-定义:
    1. 添加多个 @BuilderParam ,并定义默认值
  1. 自定义组件-使用
    1. 通过参数的形式传入多个 Builder,比如

SonCom({ titleBuilder: this.fTitleBuilder, contentBuilder: this.fContentBuilder })

6. 页面和自定义组件的生命周期

组件和页面在创建、显示、销毁的这一整个过程中,会自动执行 一系列的【生命周期钩子】,其实就是一系列的【函数】,让开发者有机会在特定的阶段运行自己的代码

页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:

  • onPageShow页面每次显示时触发一次,包括路由过程、应用进入前台等场景,如果不能触发aboutToAppear函数的时候,网络请求数据代码写在onPageShow
  • onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
  • onBackPress:当用户点击返回按钮时触发。

组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:

  • aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行,通常在这里发送网络请求获取数据
  • onDidBuild:组件build()函数执行完成之后回调该接口,开发者可以在这个阶段进行埋点数据上报等不影响实际UI的功能。不建议在onDidBuild函数中更改状态变量、这可能会导致不稳定的UI表现。
  • aboutToDisappear:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量

import { router } from '@kit.ArkUI'
interface iParams {
  name: string
  age:number
}
@Entry
@Component
struct Second {
  params:iParams = {
    name:'',
    age:0
  }

  // 1.组件生命周期
  aboutToAppear(): void {
    this.params = router.getParams() as iParams
    console.log('aboutToAppear--组件即将出现')
  }
  //2.组件生命周期
  onDidBuild() {
    console.info('onDidBuild--组件已经构建完成');
  }
  //3.页面的生命周期
  onPageShow() {
    console.info('onPageShow---页面显示');
  }
  // 4页面的生命周期
  onPageHide() {
    console.info('onPageHide--页面隐藏');
  }

  //5.组件生命周期
  aboutToDisappear() {
    console.info('aboutToDisappear--组件销毁了');
  }
  // 只有被@Entry装饰的组件才可以调用页面的生命周期
  onBackPress() {
    console.info('onBackPress--返回按钮');
    return true; // 返回true表示页面自己处理返回逻辑,不进行页面路由;返回false表示使用默认的路由返回逻辑,不设置返回值按照false处理
  }
  build() {
    Column(){
      Text(`欢迎您${this.params.name}`)
      Button('返回').onClick(()=>{
        router.back()
      })
    }
    .width('100%').height('100%').backgroundColor(Color.Pink)
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值