ArkTS
相同点
与 TypeScript 相比, 大部分语法是相同的,这里就不多赘述了
不同点
包括语法方面,和功能方面的区别
1. 语法区别
在 TypeScript 的语法基础上
- 强化了静态类型检查
- 静态分析阶段可以获取更稳定的信息
- 提升代码的健壮性和性能
- 去掉了一些过于灵活的特性,例如:
- delete 操作符
- any
- unknown
- @ts-ignore
1.1 强化了静态类型检查
- 例如:变量必须写明确的类型
- 目的:静态分析阶段可以获取更稳定的信息
1.2 去掉了一些过于灵活的特性
- 例如:any 不能使用了
- 目的:减少运行时的判断开销
2. 功能区别
在 TypeScript 的功能基础上
- 增加了
- 声明式 UI
- 状态管理支持
- 并发能力
2.1 声明式 UI
- UI 描述语法,支持用 Ts 的方式,描述 UI (HTML + CSS)
- 提供了:
- UI 描述机制
- Text
- ForEach
- ...
- 各种装饰器
- Entry 预览
- Preview 预览
- Component 自定义组件
- Builder
- ...
具体的语法细节,内容较多,所以抽出去了,请查看 UI 描述语法
2.2 状态管理
- UI 相关联的数据,支持:
- 在组件内、组件间、页面间、应用内、跨设备传递
- 状态改变,视图响应更新
- ps: 我看的课程里还没有细讲,后期补上
2.3 并发能力
- ps: 我看的课程里还没有讲到,后期补上
UI 描述语法
- 用 ts 的方式去描述 UI
内置组件
以下仅为示意,更多组件及参数 请点击
Text
Text('这是一个文本组件')
Button
Button('点击我')
Image
Image('resources/images/picture.png')
TextInput
TextInput({ placeholder: '请输入' })
Slider
Slider({ min: 0, max: 100, value: 50 })
Progress
Progress({ value: 0.5 })
Toggle
Toggle({ checked: true })
Divider
Divider()
Blank
// 空白填充组件
// 在容器主轴方向上,自动填充容器空余部分
// 仅当父组件为Row/Column/Flex时生效
Blank()
- 以上都是非容器组件,不支持往内部嵌套组件
- 以下 Column、Row、Stack、Grid、List 都是容器组件
- 支持在尾随闭包
{}
内嵌套组件
Row
Row() {
Text('学习')
Text('成长')
}
Column
Column() {
Text('学习')
Text('成长')
}
Grid
Grid() {
GridItem() { Text('单元格1') }
GridItem() { Text('单元格2') }
GridItem() { Text('单元格3') }
GridItem() { Text('单元格4') }
}
.rowsTemplate('1fr 1fr')
.columnsTemplate('1fr 1fr')
List
List() {
ListItem() {
Text('第一项')
}
ListItem() {
Text('第二项')
}
}
Scroll
Scroll() {
Column() {
Text('项目1')
Text('项目2')
Text('项目3')
Text('项目4')
}
}
Stack
// 堆叠容器,子组件按照顺序依次入栈,后一个子组件覆盖前一个子组件
Stack({ alignContent: Alignment.Bottom }) {
Text('First child, show in bottom')
Text('Second child, show in top')
}
组件的方法
通用的
// 常用的样式方法
Text(text)
.fontSize(17) // 设置字体大小
.fontWeight('bold') // 设置字体粗细
.color('#ff0000') // 设置字体颜色
.backgroundColor('#ffff00') // 设置背景颜色
.padding(10) // 设置内边距
.margin(5) // 设置外边距
.textAlign('center') // 设置文本对齐方式
.lineHeight(20) // 设置行高
.fontStyle('italic') // 设置字体样式为斜体
.textDecoration('underline') // 设置文本装饰为下划线
.borderRadius(5) // 设置边框圆角
.borderWidth(2) // 设置边框宽度
.borderColor('#0000ff') // 设置边框颜色
.width('100%') // 设置宽度
.height(50) // 设置高度
.maxWidth(200) // 设置最大宽度
.maxHeight(100) // 设置最大高度
.minWidth(100) // 设置最小宽度
.minHeight(50) // 设置最小高度
.shadowColor('#888888') // 设置阴影颜色
.shadowOffset({ x: 0, y: 2 }) // 设置阴影偏移
.shadowOpacity(0.8) // 设置阴影透明度
.shadowRadius(5); // 设置阴影半径
// 常用的事件方法
// 注意:传入非箭头函数时,需要注意 this 指向问题
Button(text)
.onClick(() => void)
TextInput()
.onChange(() => void)
.stateStyle
// 给组件增加在不同状态时的样式
// 可以使用组件内的普通变量和状态变量
Button('点我')
.stateStyles({
// 正常态
normal: {
.backgroundColor('ff2787d9')
},
// 按压态
pressed: {
.backgroundColor('ff707070')
},
// 获焦态
focused: {
.backgroundColor('#ffffeef0')
},
// 不可用态
disabled: {
.backgroundColor('ff2787d9')
},
// 选中态
selected: {
.backgroundColor('ff2787d9')
}
})
组件的装饰器
用于标记组件,给组件增加附属功能
@Entry
// 标记此页面,可以作为应用的入口页面
@Entry
struct Index {
builder() {
Text('你好')
}
}
@Component
仅能装饰 struct 关键字声明的数据结构 被装饰后具备组件化的能力,需要实现 build 方法描述 UI
// 标记这是一个组件
@Component
struct Hello {
builder() {
Text('你好')
}
}
@Page
// 标记这是一个页面
@Page
struct MyPage {
builder() {
Column() {
Text('这是一个页面');
}
}
}
@Builder
- 除了组件外,还有一种更轻量化的 UI 复用机制
- 它所装饰的函数遵循 build() 函数的语法规则,可以将重复使用的UI元素,提取成一个方法
- 传递时参数需要注意下,请参考
// 全局自定义构建函数
@Builder function overBuilder(params: { label: string }) {
Row() {
Text(`UseStateVarByValue: ${params.label} `)
}
}
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
// 私有自定义构建函数
@Builder customBuilder() {
Text(`UseStateVarByValue: ${ this.label } `)
}
build() {
Column() {
this.customBuilder()
// 必须写成对象字面量,才能引用传递
// 也就是这个状态变量的更新,才会引起它内部 UI 刷新
overBuilder({ label: this.label })
}
}
}
@Style
- 将多条样式封装成一个方法,给组件调用
- 支持在全局定义,或组件内部定义
- 组件内 @Styles 的优先级高于全局 @Styles
- 仅支持通用属性,通用事件
- 不支持传递参数
// 定义在全局的@Styles封装的样式
@Styles function globalFancy () {
.width(150)
.height(100)
.backgroundColor(Color.Pink)
}
@Entry
@Component
struct FancyUse {
@State heightValue: number = 100
// 定义在组件内的@Styles封装的样式
@Styles fancy() {
.width(200)
.height(this.heightValue)
.backgroundColor(Color.Yellow)
.onClick(() => {
this.heightValue = 200
})
}
build() {
Column({ space: 10 }) {
// 使用全局的@Styles封装的样式
Text('FancyA').globalFancy()
// 使用组件内的@Styles封装的样式
Text('FancyB').fancy()
}
}
}
@Extend
- 用于扩展原生组件的样式
- 仅支持在全局定义,不支持在组件内部定义
- 支持封装指定组件的私有属性、私有事件和自身定义的全局方法
- 支持传递参数,调用遵循TS方法传值调用
- 参数可以为状态变量,当状态变量改变时,UI可以正常的被刷新渲染
@Extend(Text) function fancy (fontSize: number) {
.fontColor(Color.Red)
.fontSize(fontSize)
}
@Entry
@Component
struct FancyUse {
build() {
Row({ space: 10 }) {
Text('Fancy').fancy(16)
Text('Fancy').fancy(24)
}
}
}
@Require
- 可用于校验,自定义组件内的各种变量,是否需要调用该自定义组件时传参
@Component
struct Child {
@Require regular_value: string = 'Hello';
@Require @State state_value: string = "Hello";
@Require @Provide provide_value: string = "Hello";
@Require @BuilderParam buildTest: () => void;
@Require @Prop message: string;
build() {
Column() {
Text(this.message).fontSize(30)
this.buildTest();
}
}
}
@Entry
@Component
struct Parent {
@State message: string = 'Hello World';
@Builder buildTest() {
Row() {
Text('Hello, world').fontSize(30)
}
}
build() {
Row() {
// 因为 Child 组件这些变量设置了@Require,所以这里必须传才行
Child({
regular_value: this.message,
state_value: this.message,
provide_value: this.message,
message: this.message,
buildTest: this.buildTest}
)
}
}
}
组件内变量的装饰器
用于给变量增加附属功能
@State
// 标记这是一个状态,状态改变时会触发重渲染
@Component
struct Counter {
@State count: number = 0;
increment() {
this.count += 1;
}
builder() {
Column() {
Text(`计数:${this.count}`);
Button('增加').onClick(() => this.increment());
}
}
}
@Prop
- 不能在 @Entry 装饰的自定义组件中使用
- 当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改
- 允许在本地修改,但修改后的变化不会同步回父组件
- 它装饰变量时会进行深拷贝,在拷贝的过程中除了基本类型、Map、Set、Date、Array外,都会丢失类型
// 标记这是一个属性,从父组件传递的值
@Component
struct Greeting {
@Props name: string;
builder() {
Text(`你好, ${this.name}`);
}
}
@Link
子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定 不能在@Entry装饰的自定义组件中使用
自定义组件
build 函数
- 所有声明在build()函数的语句,我们统称为UI描述
- 自定义组件必须实现 build 方法
- 以下是它支持的语法
// if else if
Row() {
if (Math.random() < 0.5) {
Text('小于')
} else {
Text('大于')
}
}
// forEach
ForEach(items, (item) => {
Text(item.text)
}, item => item.id)
// 点击事件
Row() {
Text('学习')
}
.onClick(() => {
console.log('哈哈哈')
})
// @Entry 装饰的,根节点唯一且必要,禁止 ForEach 作为根节点,必须为容器组件
@Entry
@Component
struct MyComponent {
build() {
Row() {
ChildComponent()
}
}
}
// Component 装饰的,根节点唯一且必要,禁止 ForEach 作为根节点,可为非容器组件
@Component
struct ChildComponent {
build() {
Image('test.jpg')
}
}
- 以下是它不支持的语法
build() {
// 反例:不允许声明本地变量
let a: number = 1;
}
build() {
// 反例:不允许console.log
console.log('print debug log');
}
build() {
// 反例:不允许本地作用域
{
...
}
}
@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())
}
}
}
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('...')
}
}
}
build() {
Column() {
// 反例:不允许使用表达式
(this.aVar > 10) ? Text('...') : Image('...')
}
}
@Component
struct CompA {
@State col1: Color = Color.Yellow;
@State col2: Color = Color.Green;
@State count: number = 1;
build() {
Column() {
// 应避免直接在Text组件内改变count的值
Text(`${this.count++}`)
.width(50)
.height(50)
.fontColor(this.col1)
.onClick(() => {
this.col2 = Color.Red;
})
Button("change col1").onClick(() =>{
this.col1 = Color.Pink;
})
}
.backgroundColor(this.col2)
}
}
// 不能在自定义组件的build()或@Builder方法里直接改变状态变量
// 反例
@State arr : Array<...> = [ ... ];
ForEach(this.arr.sort().filter(...),
item => {
...
})
// 正确的执行方式为:filter返回一个新数组,后面的sort方法才不会改变原数组this.arr
ForEach(this.arr.filter(...).sort(),
item => {
...
})
使用方式
@Component
struct Child {
private countDownFrom: number = 0;
private color: Color = Color.Blue;
build() {
}
}
@Entry
@Component
struct Parent {
private someColor: Color = Color.Pink;
build() {
Column() {
// 创建 Child 实例
// 并将成员变量 countDownFrom 初始化为 10,
// color 初始化为 this.someColor
Child({ countDownFrom: 10, color: this.someColor })
}
}
}
通用样式
// 相当于给 Child 套了一个不可见的容器组件
// 这些样式是设置在容器组件上的,而非设置给 Child 的 Button 组件
@Component
struct Child {
build() {
Button(`Hello World`)
}
}
@Entry
@Component
struct Parent {
build() {
Row() {
MyComponent2()
.width(200)
.height(300)
.backgroundColor(Color.Red)
}
}
}
生命周期
内容较多,可参考 地址
// Index.ets
import { router } from '@kit.ArkUI';
@Entry
@Component
struct MyComponent {
@State showChild: boolean = true;
@State btnColor:string = "#FF007DFF"
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onPageShow() {
console.info('Index onPageShow');
}
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onPageHide() {
console.info('Index onPageHide');
}
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onBackPress() {
console.info('Index onBackPress');
this.btnColor ="#FFEE0606"
return true // 返回true表示页面自己处理返回逻辑,不进行页面路由;返回false表示使用默认的路由返回逻辑,不设置返回值按照false处理
}
// 组件生命周期
aboutToAppear() {
console.info('MyComponent aboutToAppear');
}
// 组件生命周期
onDidBuild() {
console.info('MyComponent onDidBuild');
}
// 组件生命周期
aboutToDisappear() {
console.info('MyComponent aboutToDisappear');
}
build() {
Column() {
// this.showChild为true,创建Child子组件,执行Child aboutToAppear
if (this.showChild) {
Child()
}
// this.showChild为false,删除Child子组件,执行Child aboutToDisappear
Button('delete Child')
.margin(20)
.backgroundColor(this.btnColor)
.onClick(() => {
this.showChild = false;
})
// push到page页面,执行onPageHide
Button('push to next page')
.onClick(() => {
router.pushUrl({ url: 'pages/page' });
})
}
}
}
@Component
struct Child {
@State title: string = 'Hello World';
// 组件生命周期
aboutToDisappear() {
console.info('[lifeCycle] Child aboutToDisappear')
}
// 组件生命周期
onDidBuild() {
console.info('[lifeCycle] Child onDidBuild');
}
// 组件生命周期
aboutToAppear() {
console.info('[lifeCycle] Child aboutToAppear')
}
build() {
Text(this.title).fontSize(50).margin(20).onClick(() => {
this.title = 'Hello ArkUI';
})
}
}
工具函数
// $r形式引入应用资源,可应用于多语言场景
Text($r('app.string.title_value'))
$r('app.media.ic_default')
$rawfile('media.ic_default')
内置常量
Color.Red
FontWeight.Bold
以此类推,略...
系列文章
参考资料
写在最后
- 不是教程,只是学习记录
- 包含了一些自己的理解,一边学一边写的,难免有不对的地方
- 写出来希望能与大家探讨,看到有错误的地方,望大家指正~