鸿蒙开发-ArkTS
# 1. 什么是 ArkTS
ArkTS 围绕应用开发在 TypeScript(简称 TS)生态基础上做了进一步扩展,继承了 TS 的所有特性,是 TS 的超集。
基本语法:ArkTS 定义了声明式 UI 描述、自定义组件和动态扩展 UI 元素的能力,再配合 ArkUI 开发框架中的系统组件及其相关的事件方法、属性方法等共同构成了 UI 开发的主体。
状态管理:ArkTS 提供了多维度的状态管理机制。在 UI 开发框架中,与 UI 相关联的数据可以在组件内使用,也可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,还可以在应用全局范围内传递或跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和 UI 的联动。
渲染控制:ArkTS 提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的 UI 内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。数据懒加载从数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
ArkTS 开发是面向对象形式的开发,基于调用对象方法,进行声明式前端,最后会有底层的编译器统一编译渲染,提高效率
# 2. TypeScript 基本语法
# 1. 变量声明
let msg:string = "你好"
let flag:boolean | number = true
let p:object = {} // object 类型
let arr:Array<string> = ['1','2']
let ages:number[] = [21,18]
2
3
4
5
6
# 2. 条件控制
TS 同样也支持 if-else 和 switch 等条件控制
# 3. 循环迭代
常见循环 for i /for 循环 /for in /for of /while 循环
for (var i = 0; i < 10; i++) {
console.log(`点赞${i}次`)
}
2
3
let i = 1;
while(i <= 10){
console.log(`点赞${i}次`)
i++
}
2
3
4
5
# 4. 函数
function sayHello(name:string):void{
console.log(`${name} 你好`)
}
function sun (x:number,y:number):number{
return (x + y)
}
2
3
4
5
6
7
# 5. 类与接口
// 定义枚举
enum Msg{
HELLO = 'hello'
}
// 定义接口
interface A {
say(msg:Msg):void
}
// 实现接口
class B implements A {
say(msg:Msg):void{
console.log(msg)
}
}
// 初始化对象
let a:A = new B()
a.say(Msg.HELLO)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 6. 模块开发
每一个文件都可以是一个模块 module , 可以相互加载,提高代码复用
- 通过 export 关键词导出,import {} 引入
- 通过 export default 导出 import 取名 引入
# 3. ArkUI 常用组件
# 1. images 组件
语法: Images(src: string | PixelMap | Resource)
提示
- string 格式:通常加载网络资源,需要申请网络访问权限 (opens new window)
- PixelMap 格式:通常加载像素图,常用在图片编辑
- Resource 格式:通常加载本地图片,推荐
Images(
// 设置图片相关属性
Images('app.media.icon')
.width(100)
.height(120)
.borderRadius(10)
2
3
4
5
# 2. Text 组件
语法: Text(content?: string | Resource)
提示
- string 格式:直接填写内容
- Resource 格式:读取本地资源文件
Text($r('app.string.width_label'))
resource 目录下面 通常会有 base
en_US
zh_CN
三个主体目录,base 通常存放基础配置和资源等,en_US 和 zh_CN 是 限定词目录,针对不同国家 / 设备 / 语言,通过 键值对模式定义
# 3. TextInput 文本输入框组件
语法: TextInput({ placeholder?: ResourceStr, text?: ResourceStr })
提示
- placeholder: 提示文本
- text: 当前文本内容值
@State imagesWidth: number = 30 // 定义变量
build(){
Image($r('app.media.icon'))
.width(this.imagesWidth)
TextInput({text: this.imagesWidth.toFixed(0)}) // 数字转字符串
.width(150)
.type(InputType.Number)
.onChange(value=>{
this.imagesWidth = parseInt(value) // 字符串转整数
})
}
2
3
4
5
6
7
8
9
10
11
# 4. Button 组件
语法: Button(label?: ResourceStr)
提示
- 文字型按钮,Button (' 文字 ')
- 自定义按钮,Button (){
Image($r('app.media.search')).width(20).margin(10)
}
# 5. slider 组件
语法: Slider(options?: SliderOptions)
# 6. Column 组件 和 Row 组件
这两个组件主要是用于页面布局,采用的对齐方式为 flex 布局,对应的属性名
- justifyContent => 主轴对齐方式
- alignItems => 侧轴对齐方式
# 7. List 组件
List 组件用于列表数据,当数量过多,超出屏幕的时候,会自动提供滚动功能,List 组件既可以纵向排列,也可以横向排列
List({space:10}){
ForEach([1,2,3,4],item => {
ListItem(){ // listItem 只能包括一个根组件
Text('列表项内容')
}
})
}
.width('100%')
2
3
4
5
6
7
8
# 4. 流程循环控制
private items = [
{name:'名字',image:'1.jpg',price:3999}
]
ForEach(
arr:Array, // 要遍历的数据
(item:any, index?:number) => { // 循环生成函数
Row(){
Image(item.image)
Column(){
Text(item.name)
Text(item.price)
}
}
},
keyGenerator?:(item:any,index:number):string => {
// 键生成函数,为数组每一项生成一个唯一标识,组件是否重新渲染的判断标准
}
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 5. 自定义组件
@Entry // 入口装饰器 表示是个页面入口
@Component // 组件装饰器
struct ItemPage {
build() {
Column({space:8}){
// 标题部分
Row(){ ...code }
// 商品列表部分
ForEach(){ ...code }
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
Row(){
Images($r('app.media.ic_public_balck'))
.width(30)
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
Black() // black 组件是占满除其余组件所有剩下空间
Image($r(app.media.ic_public_refresh))
.width(30)
}
2
3
4
5
6
7
8
9
10
上述是 页面开发时 的正常标题组件代码,由于标题可能在多处地方反复使用,因此拆分成组件更为合适
组件存放目录 :
@Component
export struct Header{
private title:ResourceStr
build(){
Row(){
Images($r('app.media.ic_public_balck'))
.width(30)
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
Black() // black 组件是占满除其余组件所有剩下空间
Image($r(app.media.ic_public_refresh))
.width(30)
}
.width('100%')
.height(30)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用时,通过 import Header from '路径'
导入即可
当针对一些列表,或其他可以高度复用的代码的时候,可以通过构建函数批量创建
// 定义全局自定义函数 全局可使用
@Builder function ItemCard(item:Item){
Row(){
... 循环模块的code
}
}
2
3
4
5
6
// 定义组件内 局部函数 不采用 function 关键词创建, 定义在 build 方法内
@Builder ItemCard(item:Item){
Row(){
... 循环模块的code
}
}
// 调用的时候 需要使用 this.ItemCard()
2
3
4
5
6
7
8
针对相同部分的样式,同样可以通过装饰器抽离复用代码
// 与上诉定义函数一致, 可以定义 全局和局部 样式
@Style function fillScreen(){
.width('100%')
.height('100%')
.padding(20)
.background('#fff')
}
2
3
4
5
6
7
提示
- 通过 @Style 装饰器定义的是通用属性,类似 margin /padding/width /background 等等
- 针对继承属性相关,fontColor /fontSize 等样式,则需要通过 @Extend (Text) 来进行装饰,且不能采用定义在局部内
# 6. 状态管理
在 声明时 UI 中,是以状态驱动视图更新的
- 状态:指被装饰器所标记的变量
- 视图:基于 UI 描述渲染得到用户界面
# 1. @State 装饰器
提示
- @State 装饰器标记的标量必须初始化,不能为空值
- @State 支持 Object class string number boolean enum array 等这些类型变量
- 嵌套类型和数组中的对象属性无法触发视图更新
class Person{
name:string
age: number
constructor(name:string,age:number){
this.name = name
this.age = age
}
}
@Entry
@Component
struct Index(){
@State p:Person = new Person('Jack':21)
build(){
Column(){
Text(`${this.name} : ${this.age}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.onClick(()=>{
this.age ++
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
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
# @Prop 和 @Link 装饰器
当父子组件需要进行通讯的时候,则可以使用 @Prop 和 @Link 装饰器
:: :: | @Prop | @Link |
---|---|---|
同步类型 | 单向同步 | 双向同步: |
允许装饰的变量类型: | - @Prop 只支持 string, number, boolean, enum 类型 - 父组件对象类型,子组件是对象属性 - 不可以是数组,any | - 父子类型一直:string, number, boolean, enum, object, class, 以及他们的 array - 数组中元素增删,替换会引起刷新 - 嵌套类型以及数组中的对象属性无法触发视图更新 |
初始化方式 | 不允许子组件初始化 | 父组件传递,禁止子组件初始化 |
// 父组件
@Entry
@Component
struct Index {
// 总任务数量
@State totalNumber:number = 0
// 已完成任务数量
@State finishNumber:number = 0
// 任务数组
@State taskList:Task[] = []
build(){
Column({space:10}){
// 子组件
TashStatistics({finishNumber:this.finishNumber, totalNumber:this.totalNumber})
TaskList({finishNumber:$finishNumber, totalNumber:$totalNumber}) // $变量 表示对变量的引用,使用link的时候,需要$绑定变量
}
}
}
// 子组件
@Component
struct TashStatistics {
// 总任务数量
@Prop totalNumber:number
// 已完成任务数量
@Prop finishNumber:number
build(){
Row(){
...任务视图实现代码
}
}
}
@Component
struct TashList{
// 总任务数量
@Link totalNumber:number
// 已完成任务数量
@Link finishNumber:number
// 任务数组
@State taskList:Task[]
build(){
Row(){
...任务列表实现代码
}
}
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# @Provide 和 @Consume 装饰器
针对于非父子组件通讯的时候,也就是跨多层级组件通讯,则采用这个方式,
优点不需要写明传递参数,会自动查找匹配,缺点会消耗性能,因此能少用则少用
上述实例,可以修改成 如下
// 父组件
@Entry
@Component
struct Index {
// 总任务数量
@Provide totalNumber:number = 0
// 已完成任务数量
@Provide finishNumber:number = 0
build(){
Column({space:10}){
// 子组件
TashStatistics({finishNumber:this.finishNumber, totalNumber:this.totalNumber})
TaskList({finishNumber:$finishNumber, totalNumber:$totalNumber}) // $变量 表示对变量的引用,使用link的时候,需要$绑定变量
}
}
}
// 子组件
@Component
struct TashStatistics {
// 总任务数量
@Consume totalNumber:number
// 已完成任务数量
@Consume finishNumber:number
build(){
Row(){
...任务视图实现代码
}
}
}
@Component
struct TashList{
// 总任务数量
@Consume totalNumber:number
// 已完成任务数量
@Consume finishNumber:number
// 任务数组
@State taskList:Task[]
build(){
Row(){
...任务列表实现代码
}
}
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# @Observed 和 @ObjectLink 装饰器
主要用于 嵌套对象 和 数组元素为对象 的场景中进行双向数据同步,触发视图渲染