arkTS基础
// 变量声明
let hi: string = 'hello';
hi = 'hello,world';
// 常量声明
const hi: string = 'hello';
// ArkTS是一种静态类型语言,所有数据的类型都必须在编译时确定
// 如果一个变量或常量的声明包含了初始值,那么开发者就不需要显式指定其类型。
let h1: string = 'hello';
let h2 = 'hello';
// ArkTS提供number和Number类型,任何整数和浮点数都可以被赋给此类型的变量
let n1 = 3.14;
let n2 = 3.14159;
let n3 = .5;
let n4 = 1e2;
function factorial(n: number): number{
if(n<1){return 1}
return n*factorial(n-1);
}
let isDone: boolean = true
if(isDone){console.log('Done!')}
let s1 = 'hello world\n';
let s2 = 'this is a string';
let a = 'success';
let s3 = `the result is ${a}`;
// Object类型是所有引用类型的基类型。任何值,包括基本类型的值(它们会被自动装箱),都可以直接被赋给Object类型的变量。
// 数组中第一个元素的索引为0。
let name:string[] = ['Alice','Bob','Carol'];
enum ColorSet {Red ,Green ,Blue}
enum ColorSet {White=0xFF,Grey=0x7F,Black=0x00}
let c: ColorSet = ColorSet.Red;
// union类型,即联合类型,是由多个类型组合成的引用类型。
class Cat {name:string = 'cat'}
class Dog {name:string = 'dog'}
type Animal = Cat | Dog | number
let animal:Animal = new Cat();
animal = 42
// 比较运算符
=== // 严格相等
!== // 严格不相等
== // 操作数相等
!= // 操作数不相等
if语句
if (condition1){}
else if (condition2){}
else (condition3) {}
// 条件表达式可以是任何类型。但是对于boolean以外的类型,会进行隐式类型转换
let s1 = 'hello'
if (s1) {console.log(s1)}
Switch语句
switch (expression){
case label1: break;
case label2: break;
default: console.log('default');
}
条件表达式
condition ? expression1 : expression2
let isValid = Math.random() > 0.5 ? true : false;
For语句
for(let i=0; i<10; i+=2){
sum += i;
}
for(let ch of 'a string object'){
console.log(ch);
}
Throw和Try语句
throw new Error('this error')
try{
}catch(e){
error = e as Error;
console.log(`message='${error.message}'`)
}finally{
}
函数
function add(x:string, y:string): string{
let z: string = `${x}${y}`;
return z;
}
可选参数
function hello(name?: string, age: number = 2): string{
if(name==undefine){console.log('hello');}
else if(name){console.log(`hello,${name}`);}
}
//函数的最后一个参数可以是rest参数。使用rest参数时,允许函数或方法接受任意数量的实参。
Rest参数
function sum(...numbers: number[]):number{
let res = 0;
for(let n of numbers){
res += n;
}
return res;
}
// 如果可以从函数体内推断出函数返回类型,则可在函数声明中省略标注返回类型。
function goo() { return 'goo'; }
函数类型
// 函数类型通常用于定义回调:
type trigFunc = (x:number) => number;
function do_action(f: trigFunc){
f(3.14);
}
do_action(Math.sin);
箭头函数(又名Lambda函数)
let sum = (x: number, y: number): number => {return x+y;}
闭包
闭包是由函数及声明该函数的环境组合而成的,包含了这个闭包创建时作用域内的任何局部变量
function f(): () => number{
let count = 0;
let g = ():number => {count++;return count;};
return g;
}
函数重载
function foo(x: number):void;
function foo(x: string):void;
function foo(x: number | string):void {
}
// ArkTS要求所有字段在声明时或者构造函数中显式初始化。
类
class Person {
static numberOfPersons = 0;
name: string = '';
surname: string = '';
construct(n: string, sn: surname){
this.name = n;
this.surname = sn;
}
fullName(): string {
return name + ' ' + surname;
}
static stMethod(): string{
return 'this is static method.'
}
}
let p = new Person('John','Smith');
let q: Person = {name: 'john',surname: 'Smith'};
p.fullname();
Person.numberOfPersons
Person.stMethod()
getter和setter
class Person {
name:string = '';
private _age: number = 0;
get age(): number {return this._age}
set age(x: number) {
if(x<0>){
throw Error('Invalid age argument')
}
this._age = x;
}
}
继承
class Person {
name: string = '';
state: string = '';
constructor (n: string, st: string){
this.name = n;
this.state = st;
}
move(){
}
}
interface Work {
Work();
}
class Employee extend Person{
salary: number = 3000
constructor(n: string, st: string, sa: number){
super(n,st);
this.salary = sa;
}
Work(){
super.move();
}
}
方法重载
class C {
constructor(x: number): void // 构造函数1
constructor(x: string): void // 构造函数2
constructor(x: number | string): void{ // 构造函数实现
}
}
可见性
// public修饰的类成员(字段、方法、构造函数)在程序的任何可访问该类的地方都是可见的。
// private修饰的成员不能在声明该成员的类之外访问
// protected修饰符的作用与private修饰符非常相似,不同点是protected修饰的成员允许在派生类中访问
class C {
public x: number = 0;
private m: string = '';
protected n: string = '';
}
对象字面量
// 对象字面量是一个表达式,可用于创建类实例并提供一些初始值。它在某些情况下更方便,可以用来代替new表达式。
class C {
x: number = 0;
s: string = '';
}
function foo
let c: C = {x: 42, s: 'foo'}
c = {x: 42, s: 'foo'}
// 默认情况下,ArkTS中的所有类型都是不可为空
let x: number = null; // 编译时错误
let x: number | null = null;
x = 1; // ok
x = null; // ok
function foo(a: A | null) {
a.value; // 编译时错误:无法访问可空值的属性
a!.value; // 编译通过,如果运行时a的值非空,可以访问到a的属性;如果运行时a的值为空,则发生运行时异常
}
a ?? b等价于三元运算符(a != null && a != undefined) ? a : b。
导入、导出
export class P {}
export function A() {}
export let x: number = 0
import * as Utils from './utils'
import { X, Y } from './utils'
动态导入
// say.ts
export function hi() {
console.log('Hello');
}
async function test() {
let ns = await import('./say');
let hi = ns.hi;
hi();
}
// 顶层语句是指在模块的最外层直接编写的语句,这些语句不被包裹在任何函数、类、块级作用域中。顶层语句包括变量声明、函数声明、表达式等。
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的声明式开发范式。
// 根据组件构造方法的不同,创建组件包含有参数和无参数两种方式。创建组件时不需要new运算符。
Column() {
Text('item 1')
Divider()
Image('https://xyz/test.jpg')
}
配置属性
// 属性方法以“.”链式调用的方式配置系统组件的样式和其他属性,建议每个属性方法单独写一行。
Image('test.jpg')
.alt('error.jpg')
.width(100)
.height(100)
配置事件
Button('Click me')
.onClick(() => {
this.myText = 'ArkUI';
})
配置子组件
// Column、Row、Stack、Grid、List等组件都是容器组件。
Column() {
Row() {
Image('test1.jpg')
.width(100)
.height(100)
Button('click +1')
.onClick(() => {
console.info('+1 clicked!');
})
}
}
自定义组件
// 如果在另外的文件中引用该自定义组件,需要使用export关键字导出,并在使用的页面import该自定义组件。
// @Component装饰器仅能装饰struct关键字声明的数据结构。struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI,一个struct只能被一个@Component装饰。
// @Reusable:@Reusable装饰的自定义组件具备可复用能力
@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!';
})
}
}
}
组建嵌套参数传递
@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禁止作为根节点。
* 不允许声明本地变量
build() {
// 反例:不允许声明本地变量
let a: number = 1;
}
* 不允许调用没有用@Builder装饰的方法
* 不允许使用switch语法
* 不允许直接改变状态变量
*
自定义组件通用样式
@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所处的开发者不可见的容器组件上。
**/
页面和自定义组件生命周期
- 自定义组件:@Component装饰的UI单元,可以组合多个系统组件实现UI的复用,可以调用组件的生命周期。
- 页面:即应用的UI页面。可以由一个或者多个自定义组件组成,@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry。只有被@Entry装饰的组件才可以调用页面的生命周期。
页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:
- onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。
- onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
- onBackPress:当用户点击返回按钮时触发。
组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:
- aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
- onDidBuild:组件build()函数执行完成之后回调该接口,不建议在onDidBuild函数中更改状态变量、使用animateTo等功能,这可能会导致不稳定的UI表现。
- aboutToDisappear:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。
@Entry装饰的组件(页面)生命周期
自定义组件重新渲染
当事件句柄被触发(比如设置了点击事件,即触发点击事件)改变了状态变量时,或者LocalStorage / AppStorage中的属性更改,并导致绑定的状态变量更改其值时,将启动重新渲染
if/else条件渲染
if、else if后跟随的条件语句可以使用状态变量或者常规变量(状态变量:值的改变可以实时渲染UI,常规变量:值的改变不会实时渲染UI)。
更新机制
- 评估if和else if的状态判断条件,如果分支没有变化,无需执行以下步骤。如果分支有变化,则执行2、3步骤:
- 删除此前构建的所有子组件。
- 执行新分支的构造函数,将获取到的组件添加到if父容器中。如果缺少适用的else分支,则不构建任何内容。