Bootstrap

arkTS基础

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分支,则不构建任何内容。
;