04:ArkTS语言
基础语法之变量声明+数据类型
- js:JavaScript
- ts:TypeScript
- ets: extend TypeScript
变量名要求:
- 变量名称可以包含数字和字母。
- 变量名不能以数字开头。
- 除了下划线 _ 和美元 $ 符号外,不能包含其他特殊字符,包括空格。
- 不能使用关键字和保留字(已经定义了特殊含义的单词)
- 见名知意
变量可以先声明后赋值,也可以声明的时候直接赋值(初始化)
数据类型
TypeScript支持一些基础的数据类型,如布尔型、数组、字符串等
数字
TypeScript里的所有数字都是浮点数,这些浮点数的类型是 number
。除了支持十进制,还支持二进制、八进制、十六进制。
// 数值类型
let num1: number = 18 //十进制
let num2: number = 0b10011 //ob 二进制
let num3: number = 0o1234567 // 0o 八进制
let num4: number = 0x123abcdef //0x 十六进制
字符串
TypeScript里使用 string
表示文本数据类型, 可以使用双引号( "
)或单引号('
)表示字符串或者反引号(```)。
反引号中可以配合$解析变量
// 字符串类型
let str1: string = 'HarmonyOS4.0'
let str2: string = "ArkTS"
let str3: string = `帝心今年${num1}岁了` //tab上方飘号键盘...
布尔值
TypeScript中可以使用boolean
来表示这个变量是布尔值,可以赋值为true
或者false
。
// 布尔类型 true false
let stateOn: boolean = true
let stateOff: boolean = false
联合类型
联合类型(Union Types)表示取值可以为多种类型中的一种。
如果当下还没想好用哪个类型...
let meiXiangHao: string | number |boolean
meiXiangHao = 'seven'
meiXiangHao = 7
meiXiangHao = true
数组
TypeScrip有两种方式可以定义数组。
第一种方式是使用数组泛型,Array< 元素类型 >。
// 数组
let course1: Array<string> = ['入门',"基础语法",`组件`]
第二种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组。
let course2: string[] = ['动画',"网络",`存储`]
枚举
enum类型是对JavaScript标准数据类型的一个补充,使用枚举类型可以为一组数值赋予友好的名字。
enum Color {Red, Green, Blue};
let c: Color = Color.Green;
元组
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string
和number
类型的元组。
let x: [string, number];
x = ['hello', 10]; // OK
x = [10, 'hello']; // Error
Unknown
有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。那么我们可以使用unknown类型来标记这些变量。(同any
)
let notSure: unknown = 4;
notSure = 'maybe a string instead';
notSure = false;
Void
当一个函数没有返回值时,你通常会见到其返回值类型是 void。
function test(): void {
console.log('This is function is void');
}
Null 和 Undefined
TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null。
let u: undefined = undefined;
let n: null = null;
数据类型应用
// todo:本套教程QQ交流群:324702418
// todo: 代码在线地址:https://gitee.com/mayuanwei
// 数值类型
let num1: number = 3.1415
let num2: number = 0b10100
let num3: number = 0o107
let num4: number = 0x012abcf
let num5: number = 10+ 5
// 字符串
let str1:string = 'HarmonyOS' + "ArkTS"
let str2:string = "HarmonyOS"
let str3:string = `圆周率:${num1}`
// 布尔boolean
let stateOn: boolean = true
let stateOff: boolean = false
// 数组 Array 索引值0
let names1:string[] = ['帝心','庄生']
let names2:Array<string> = ['帝心','庄生']
// 枚举类型
enum myColor{
Red = 'red',
Green ='green'
}
@Entry
@Component
struct DataType {
pome:string = '劝君莫惜金缕衣 劝君惜取少年时'
build() {
Row() {
Column() {
Text(names1[1])
.fontSize(50)
.fontWeight(FontWeight.Bold)
.fontColor(myColor.Red)
Divider()
if (stateOff){
Text(num1.toString())
.fontSize(50)
.fontWeight(FontWeight.Bold)
}else{
Text(num2.toString())
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
Divider()
Text(this.pome)
}
.width('100%')
}
.height('100%')
}
}
声明式UI描述
ArkTS以声明方式组合和扩展组件来描述应用程序的UI,同时还提供了基本的属性、事件和子组件配置方法,帮助开发者实现应用交互逻辑。
创建组件
根据组件构造方法的不同,创建组件包含有参数和无参数两种方式。
说明
创建组件时不需要new运算符。
无参数
如果组件的接口定义没有包含必选构造参数,则组件后面的“()
”不需要配置任何内容。例如,Divider
组件不包含构造参数:
Column() {
Text('item 1')
Divider()
Text('item 2')
}
有参数
如果组件的接口定义包含构造参数,则在组件后面的“()
”配置相应参数。
- Image组件的必选参数src。
-
Image('https://xyz/test.jpg')
-
官方说如果应用是网络图片地址。需要在
src/main/module.json5
中申请网络权限。方式如下。而实际上我们不申请,依旧可以用网络图片。哥们想不通。算一个小bug?"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
- Text组件的非必选参数content。
// string类型的参数
Text('test')
// $r形式引入应用资源,可应用于多语言场景
Text($r('app.string.title_value'))
// 无参数形式
Text()
- 变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求。
例如,设置变量或表达式来构造Image和Text组件的参数。
Image(this.imagePath)
Image('https://' + this.imageUrl)
Text(count: ${this.count})
配置属性
属性方法以“.
”链式调用的方式配置系统组件的样式和其他属性,建议每个属性方法单独写一行。
- 配置Text组件的字体大小。
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)
.objectFit(ImageFit.Cover)
- 对于系统组件,ArkUI还为其属性预定义了一些枚举类型供开发者调用,枚举类型可以作为参数传递,但必须满足参数类型要求。
例如,可以按以下方式配置Text组件的颜色和字体样式。
Text('hello')
.fontSize(20)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
配置事件
事件方法以“.
”链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行。
- 使用箭头函数配置组件的事件方法。
Button('Click me')
.onClick(() => {
this.myText = 'ArkUI';
})
- 使用匿名函数表达式配置组件的事件方法,要求使用bind,以确保函数体中的this指向当前组件。
Button('add counter')
.onClick(function(){
this.counter += 2;
}.bind(this))
- 使用组件的成员函数配置组件的事件方法。
myClickHandler(): void {
this.counter += 2;
}
...
Button('add counter')
.onClick(this.myClickHandler.bind(this))
配置子组件
如果组件支持子组件配置,则需在尾随闭包"{...}
"中为组件添加子组件的UI描述。Column
、Row
、Stack
、Grid
、List
等组件都是容器组件。
- 以下是简单的
Column
组件配置子组件的示例。
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!');
})
}
}
自定义组件
创建自定义组件
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。
诗文学习案例效果图
自定义组件具有以下特点:
- 可组合:允许开发者组合使用系统组件、及其属性和方法。
- 可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用。
- 数据驱动UI更新:通过状态变量的改变,来驱动UI的刷新。
自定义组件的基本用法
以下示例展示了自定义组件的基本用法。
//src/main/ets/pages/CustomComponents.ets
// 创建自定义组件
@Component
struct itemComponent {
// 自定义组件可以使用变量(都是私有化的) 传递参数
content: string = '青山隐隐水迢迢,秋尽江南草未凋。'
// 哪种情况可以驱动UI更新。@state
@State isDone: boolean = false
build() {
// 必须有一个根组件。
Row() {
Image(this.isDone ? $r('app.media.todo_ok') : $r('app.media.todo_default'))
.width(20)
.height(20)
.margin(15)
Text(this.content)
.decoration({ type: this.isDone ? TextDecorationType.LineThrough : TextDecorationType.None })
}
.backgroundColor(Color.Pink)
.borderRadius(25)
.margin({
top: 15
})
.onClick(() => {
this.isDone = !this.isDone
})
}
}
itemComponent
可以在其他自定义组件中的build()
函数中多次创建,实现自定义组件的重用。
@Entry
@Component
struct CustomComponents {
@State message: string = '诗词学习'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
itemComponent()
itemComponent({ content: '二十四桥明月夜,玉人何处教吹箫?' })
itemComponent({ content: '荷尽已无擎雨盖,菊残犹有傲霜枝。' })
itemComponent({ content: '一年好景君须记,最是橙黄橘绿时。' })
}
.width('100%')
}
.height('100%')
}
}
要完全理解上面的示例,需要了解自定义组件的以下概念定义,本文将在后面的小节中介绍:
自定义组件的基本结构
- struct:自定义组件基于struct实现,
struct
+ 自定义
组件名 +{...}
的组合构成自定义组件,不能有继承关系。对于struct的实例化,可以省略new。
说明:自定义组件名、类名、函数名不能和系统组件名相同。
- @Component:@Component装饰器仅能装饰struct关键字声明的数据结构。struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI,一个struct只能被一个@Component装饰。
说明:从API version 9开始,该装饰器支持在ArkTS卡片中使用。
@Component
struct MyComponent {
}
- build()函数:build()函数用于定义自定义组件的声明式UI描述,自定义组件必须定义build()函数。
@Component
struct MyComponent {
build() {
}
}
- @Entry:@Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中,最多可以使用@Entry装饰一个自定义组件。@Entry可以接受一个可选的LocalStorage的参数。
说明:从API version 9开始,该装饰器支持在ArkTS卡片中使用。
@Entry
@Component
struct MyComponent {
}
成员函数/变量
自定义组件除了必须要实现build()
函数外,还可以实现其他成员函数 ,成员函数具有以下约束:
- 不支持静态函数。
- 成员函数的访问是私有的。
自定义组件可以包含成员变量,成员变量具有以下约束:
- 不支持静态成员变量。
- 所有成员变量都是私有的,变量的访问规则与成员函数的访问规则相同。
- 自定义组件的成员变量本地初始化有些是可选的,有些是必选的。具体是否需要本地初始化,是否需要从父组件通过参数传递初始化子组件的成员变量,请状态管理。
自定义组件的参数规定
从上文的示例中,我们已经了解到,可以在build
方法或者@Builder装饰的函数里创建自定义组件,在创建自定义组件的过程中,根据装饰器的规则来初始化自定义组件的参数。
@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 })
}
}
}
build()函数
所有声明在build()
函数的语言,我们统称为UI描述,UI描述需要遵循以下规则:
- @Entry装饰的自 定义组件,其build()函数下的根节点唯一且必要,且必须为容器组件,其中ForEach禁止作为根节点。
- @Component装饰的自定义组件,其build()函数下的根节点唯一且必要,可以为非容器组件,其中ForEach禁止作为根节点。
@Entry
@Component
struct MyComponent {
build() {
// 根节点唯一且必要,必须为容器组件
Row() {
ChildComponent()
}
}
}
@Component
struct ChildComponent {
build() {
// 根节点唯一且必要,可为非容器组件
Image('test.jpg')
}
}
- 不允许声明本地变量,反例如下。
build() {
// 反例:不允许声明本地变量
let a: number = 1;
}
- 不允许在UI描述里直接使用console.info,但允许在方法或者函数里使用,反例如下。
build() {
// 反例:不允许console.info
console.info('print debug log');
}
- 不允许创建本地的作用域,反例如下。
build() {
// 反例:不允许本地作用域
{
...
}
}
- 不允许调用没有用@Builder装饰的方法,允许系统组件的参数是TS方法的返回值。
@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。反例如下。
build() {
Column() {
// 反例:不允许使用switch语法
switch (expression) {
case 1:
Text('...')
break;
case 2:
Image('...')
break;
default:
Text('...')
break;
}
}
}
- 不允许使用表达式,反例如下。
build() {
Column() {
// 反例:不允许使用表达式
(this.aVar > 10) ? Text('...') : Image('...')
}
}
自定义组件通用样式
自定义组件通过“.
”链式调用的形式设置通用样式。
@Component
struct MyComponent2 {
build() {
Button(Hello World)
}
}
@Entry
@Component
struct MyComponent {
build() {
Row() {
MyComponent2()
.width(200)
.height(300)
.backgroundColor(Color.Red)
}
}
}
说明
ArkUI给自定义组件设置样式时,相当于给MyComponent2套了一个不可见的容器组件,而这些样式是设置在容器组件上的,而非直接设置给MyComponent2的Button组件。通过渲染结果我们可以很清楚的看到,背景颜色红色并没有直接生效在Button上,而是生效在Button所处的开发者不可见的容器组件上。
@Builder装饰器:自定义组件构建函数
前面章节介绍了如何创建一个自定义组件。该自定义组件内部UI结构固定,仅与使用方进行数据传递。ArkUI还提供了一种更轻量的UI元素复用机制@Builder
,@Builder
所装饰的函数遵循build()
函数语法规则,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。
为了简化语言,我们将@Builder装饰的函数也称为“自定义构建函数”。
说明
从API version 9开始,该装饰器支持在ArkTS卡片中使用。
案例
组件内自定义构建函数
/*
* 组件内:自定义构建函数
* */
@Entry
@Component
struct BuilderUI {
@State message: string = '@Builder'
@State isDone: boolean = false
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
this.item('劝君莫惜金缕衣 劝君惜取少年时' )
}
.width('100%')
}
.height('100%')
}
@Builder item(content: string) {
Row() {
Image( this.isDone ? $r('app.media.todo_ok') : $r('app.media.todo_default'))
.width(20)
.height(20)
.margin(15)
Text(content)
.decoration({ type: this.isDone ? TextDecorationType.LineThrough : TextDecorationType.None })
}
.width('100%')
.backgroundColor(Color.Pink)
.borderRadius(25)
.margin({
top: 15
})
.onClick(() => {
this.isDone = !this.isDone
})
}
}
组件外自定义构建函数
/*
* 组件外:自定义构建函数
* 需要function关键字
* */
@Entry
@Component
struct BuilderUI {
@State message: string = '@Builder'
@State isDone: boolean = false
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
item('劝君莫惜金缕衣 劝君惜取少年时')
}
.width('100%')
}
.height('100%')
}
}
@Builder function item(content: string) {
Row() {
Image( this.isDone ? $r('app.media.todo_ok') : $r('app.media.todo_default'))
.width(20)
.height(20)
.margin(15)
Text(content)
.decoration({ type: this.isDone ? TextDecorationType.LineThrough : TextDecorationType.None })
}
.width('100%')
.backgroundColor(Color.Pink)
.borderRadius(25)
.margin({
top: 15
})
.onClick(() => {
this.isDone = !this.isDone
})
}
装饰器使用说明
自定义组件内自定义构建函数
定义的语法:
@Builder MyBuilderFunction({ ... })
使用方法:
this.MyBuilderFunction({ ... })
- 允许在自定义组件内定义一个或多个
@Builder
方法,该方法被认为是该组件的私有、特殊类型的成员函数。 - 自定义构建函数可以在所属组件的
build方
法和其他自定义构建函数中调用,但不允许在组件外调用。 - 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。
全局自定义构建函数
定义的语法:
@Builder function MyGlobalBuilderFunction({ ... })
使用方法:
MyGlobalBuilderFunction()
- 全局的 自定义构建函数可以被整个应用获取,不允许使用
this
和bind
方法。 - 如果不涉及组件状态变化,建议使用全局的自定义构建方法。
参数传递规则
自定义构建函数的参数传递有按值传递和按引用传递两种,均需遵守以下规则:
- 参数的类型必须与参数声明的类型一致,不允许
undefined
、null
和返回undefined
、null
的表达式。 - 在自定义构建函数内部,不允许改变参数值。如果需要改变参数值,且同步回调用点,建议使用@Link。
- @Builder内UI语法遵循UI语法规则。