• HarmonyOS学习 -- ArkTS开发语言入门


    一、编程语言介绍

    ArkTS是HarmonyOS主力应用开发语言。它在TypeScript(简称TS)的基础上,匹配ArkUI框架,扩展了声明式UI、状态管理等相应的能力,让开发者以更简洁、更自然的方式开发跨端应用。

    • JavaScript是一种属于网络的高级脚本语言,已经被广泛应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。
    • TypeScript是JavaScript的一个超集,它扩展了JavaScript的语法,通过在JavaScript的基础上添加静态类型定义构建而成,是一个开源的编程语言。
    • ArkTS基于TypeScript语言,扩展了声明式UI、状态管理、并发任务等能力。

    在这里插入图片描述

    二、TypeScript基础类型

    1. 布尔值

    let isDone: boolean = false;
    
    • 1

    2. 数字

    TypeScript里的所有数字都是浮点数,这些浮点数的类型是number。除了支持十进制,还支持二进制、八进制、十六进制

    let decLiteral: number = 2023;
    let binaryLiteral: number = 0b11111100111;
    let octalLiteral: number = 0o3747;
    let hexLiteral: number = 0x7e7;
    console.log('decLiteral is' + decLiteral)
    // 结果都是2023
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. 字符串

    let name: string = "Jacky";
    
    • 1

    4. 数组

    TypeSscript支持两种方式声明数组:

    let list1: number[] = [1, 2, 3];
    let list2: Array = [1, 2, 3];
    
    • 1
    • 2

    5. 元组

    let x: [string, number];
    x = ['hello', 10]
    
    • 1
    • 2

    6. 枚举

    enum Color {Red, Green, Blue};
    let c: Color = Color.Green;
    
    • 1
    • 2

    7. unknown

    有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。那么我们可以使用 unknown 类型来标记这些类型

    let notSure: unknown = 4;
    notSure = 'maybe a string instead';
    notSure = false;
    
    • 1
    • 2
    • 3

    8. void

    当一个函数没有返回值时,通常会见到其返回值类型是void。

    function test() void {
    console.log('This is function is void');
    }
    
    • 1
    • 2
    • 3

    9. null 和 undefined

    TypeScript里,undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 unll。

    let u: undefined = undefined;
    let n: null = null;
    
    • 1
    • 2

    10. 联合类型

    联合类型(Union Types)表示取值可以为多种类型中的一种。

    let myFavoriteNumber: string | number;;
    myFavoriteNumber = 'seven';
    myFavoriteNumber = 7;
    
    • 1
    • 2
    • 3

    三、TypeScript基础知识

    条件语句

    if语句

    let num: number = 5;
    if (num > 0) {
      console.log('数字是正数');
    }
    
    • 1
    • 2
    • 3
    • 4
    let num: number = 12;
    if (num % 2 == 0) {
      console.log('数字是偶数');
    } else {
      console.log('数字是奇数');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    let num: number = 5;
    if (num > 0) {
      console.log(num + '是正数');
    } else if (num < 0) {
      console.log(num + '是负数');
    } else {
      console.log(num + '是0');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    switch语句

    var grade: string = 'A';
    switch(grade) {
      case 'A': {
        console.log('优');
        break;
      }
      case 'B': {
        console.log('良');
        break;
      }
      case 'C': {
        console.log('及格');
        break;
      }
      case 'D': {
        console.log('不及格');
        break;
      }
      default: {
        console.log('非法输入');
        break;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    函数定义

    有名函数和匿名函数

    // 有名函数:给变量设置为number类型
    function add(x: number, y: number): number {
      return x + y;
    }
    
    // 匿名函数:给变量设置为number类型
    let myAdd = function (x: number, y: number): number {
      return x + y;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可选参数

    function buildName(firstName: string, lastName?: string) {
        if (lastName)
            return firstName + ' ' + lastName;
        else
            return firstName;
    }
    
    let result1 = buildName('Bob');
    let result2 = buildName('Bob', 'Adams'); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    剩余参数

    剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 可以使用省略号( …)进行定义:

    function getEmployeeName(firstName: string, ...restOfName: string[]) {
      return firstName + ' ' + restOfName.join(' ');
    }
    
    let employeeName = getEmployeeName('Joseph', 'Samuel', 'Lucas', 'MacKinzie');
    
    • 1
    • 2
    • 3
    • 4
    • 5

    箭头函数

    ES6版本的TypeScript提供了一个箭头函数,它是定义匿名函数的简写语法,用于函数表达式,它省略了function关键字。箭头函数的定义如下,其函数是一个语句块:

    ( [param1, parma2,…param n] )=> {
        // 代码块
    }
    
    • 1
    • 2
    • 3

    其中,括号内是函数的入参,可以有0到多个参数,箭头后是函数的代码块。我们可以将这个箭头函数赋值给一个变量,如下所示:

    let arrowFun = ( [param1, parma2,…param n] )=> {
        // 代码块
    }
    
    • 1
    • 2
    • 3

    如果要主动调用这个箭头函数,可以按如下方法去调用:

    arrowFun(param1, parma2,…param n)
    
    • 1

    1. 类的定义

    TypeScript支持基于类的面向对象的编程方式,定义类的关键字为class,后面紧跟类名。类描述了所创
    建的对象共同的属性和方法。
    声明一个Person类,这个类有3个成员:一个是属性(包含name和age),一个是构造函数,一个是getPersonInfo方法,其定义如下所示。

    class Person {
      private name: string
      private age: number
    
      constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
      }
    
      public getPersonInfo(): string {
        return `My name is ${this.name} and age is ${this.age}`;
      }
    }
    
    let person1 = new Person('Jacky', 18);
    person1.getPersonInfo();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2. 继承

    继承就是子类继承父类的特征和行为,使得子类具有父类相同的行为。TypeScript中允许使用继承来扩展现有的类,对应的关键字为extends。

    class Employee extends Person {
      private department: string
    
      constructor(name: string, age: number, department: string) {
        super(name, age);
        this.department = department;
      }
    
      public getEmployeeInfo(): string {
        return this.getPersonInfo() + ` and work in ${this.department}`;
      }
    }
    
    let person2 = new Employee('Tom', 28, 'HuaWei');
    person2.getPersonInfo();
    person2.getEmployeeInfo();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    模块

    随着应用越来越大,通常要将代码拆分成多个文件,即所谓的模块(module)。模块可以相互加载,并可以使用特殊的指令 export 和 import 来交换功能,从另一个模块调用一个模块的函数。
    两个模块之间的关系是通过在文件级别上使用 import 和 export 建立的。模块里面的变量、函数和类等在模块外部是不可见的,除非明确地使用 export 导出它们。类似地,我们必须通过 import 导入其他模块导出的变量、函数、类等。

    • 导出
    export class NewsData {
      title: string;
      content: string;
      imagesUrl: Array;
      source: string;
    
      constructor(title: string, content: string, imagesUrl: Array, source: string) {
        this.title = title;
        this.content = content;
        this.imagesUrl = imagesUrl;
        this.source = source;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 导入
    import { NewsData } from '../common/bean/NewsData';
    
    • 1

    迭代器

    当一个对象实现了Symbol.iterator属性时,我们认为它是可迭代的。一些内置的类型如Array,Map,Set,String,Int32Array,Uint32Array等都具有可迭代性。

    for…of 语句

    let someArray = [1, "string", false];
    
    for (let entry of someArray) {
        console.log(entry); // 1, "string", false
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    for…in 语句

    let list = [4, 5, 6];
    
    for (let i in list) {
        console.log(i); // "0", "1", "2",
    }
    
    for (let i of list) {
        console.log(i); // "4", "5", "6"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    四、ArkTS基础知识

    UI规范描述

    ArkUI开发框架

    在这里插入图片描述
    ArkTS声明式开发范式
    在这里插入图片描述

    基本组成说明如下:

    • 装饰器
      用来装饰类、结构体、方法以及变量,赋予其特殊的含义,如上述示例中 @Entry 、 @Component 、 @State 都是装饰器。具体而言, @Component 表示这是个自定义组件; @Entry 则表示这是个入口组件; @State 表示组件中的状态变量,此状态变化会引起 UI 变更。

    • 自定义组件
      可复用的 UI 单元,可组合其它组件,如上述被 @Component 装饰的 struct Hello。

    • UI 描述
      声明式的方式来描述 UI 的结构,如上述 build() 方法内部的代码块。

    • 内置组件
      框架中默认内置的基础和布局组件,可直接被开发者调用,比如示例中的 Column、Text、Divider、Button。

    • 事件方法
      用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在Button后面的onClick()。

    • 属性方法
      用于组件属性的配置,统一通过属性方法进行设置,如fontSize()、width()、height()、color() 等,可通过链式调用的方式设置多项属性。
      从UI框架的需求角度,ArkTS在TS的类型系统的基础上,做了进一步的扩展:定义了各种装饰器、自定义组件和UI描述机制,再配合UI开发框架中的UI内置组件、事件方法、属性方法等共同构成了应用开发的主体。
      在应用开发中,除了UI的结构化描述之外,还有一个重要的方面:状态管理。如上述示例中,用 @State 装饰过的变量 myText ,包含了一个基础的状态管理机制,即 myText 的值的变化会自动触发相应的 UI 变更 (Text组件)。ArkUI 中进一步提供了多维度的状态管理机制。和 UI 相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间,爷孙组件之间,也可以是全局范围内的传递,还可以是跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和 UI 的联动。

    总体而言,ArkUI开发框架通过扩展成熟语言、结合语法糖或者语言原生的元编程能力、以及UI组件、状态管理等方面设计了统一的UI开发范式,结合原生语言能力共同完成应用开发。这些构成了当前ArkTS基于TS的主要扩展。

    渲染控制

    • 条件渲染:使用if/else进行条件渲染。
    • 循环渲染:开发框架提供循环渲染(ForEach组件)来迭代数组,并为每个数组项创建相应的组件。

    状态管理

    组件状态管理装饰器用来管理组件中的状态,它们分别是:@State、@Prop、@Link。

    • @State装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。
    • @Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但更改不会通知给父组件,即@Prop属于单向数据绑定。
    • @Link装饰的变量可以和父组件的@State变量建立双向数据绑定,需要注意的是:@Link变量不能在组件内部进行初始化。
    • @Builder装饰的方法用于定义组件的声明式UI描述,在一个自定义组件内快速生成多个布局内容。
      @State、@Prop、@Link三者关系如图所示:
      在这里插入图片描述

    组件生命周期函数:

    自定义组件的生命周期函数用于通知用户该自定义组件的生命周期,这些回调函数是私有的,在运行时由开发框架在特定的时间进行调用,不能从应用程序中手动调用这些回调函数。
    自定义组件生命周期的简化图示:
    在这里插入图片描述

    五、ArkTS实践

    1. 声明式UI基本概念

    应用界面是由一个个页面组成,ArkTS是由ArkUI框架提供,用于以声明式开发范式开发界面的语言。

    声明式UI构建页面的过程,其实是组合组件的过程,声明式UI的思想,主要体现在两个方面:

    • 声明式描述:描述UI的呈现结果,而不关心过程
    • 状态驱动视图更新

    类似苹果的SwiftUI中通过组合视图View,安卓Jetpack Compose中通过组合@Composable函数,ArkUI作为HarmonyOS应用开发的UI开发框架,其使用ArkTS语言构建自定义组件,通过组合自定义组件完成页面的构建。

    2. 自定义组件的组成

    ArkTS通过struct声明组件名,并通过@Component和@Entry装饰器,来构成一个自定义组件。
    使用@Entry和@Component装饰的自定义组件作为页面的入口,会在页面加载时首先进行渲染。

    @Entry
    @Component
    struct ToDoList {...}
    
    • 1
    • 2
    • 3

    使用@Component装饰的自定义组件,如ToDoItem这个自定义组件则对应如下内容,作为页面的组成部分。

    @Component
    struct ToDoItem {...}
    
    • 1
    • 2

    在自定义组件内需要使用build方法来进行UI描述。

    @Entry
    @Component
     struct ToDoList
       ...
       build() {
        ...
      } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    build方法内可以容纳内置组件和其他自定义组件,如Column和Text都是内置组件,由ArkUI框架提供,ToDoItem为自定义组件,需要开发者使用ArkTS自行声明。

    @Entry
    @Component
    struct ToDoList {
      ...
      build() {
        Column(...) {
          Text(...)
            ...
          ForEach(...{
            TodoItem(...)
          },...)
        }
      ...
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3. 配置属性与布局

    自定义组件的组成使用基础组件和容器组件等内置组件进行组合。但有时内置组件的样式并不能满足我们的需求,ArkTS提供了属性方法用于描述界面的样式。

    • 常量传递
      例如使用fontSize(50)来配置字体大小。
    Text('Hello World').fontSize(50)
    
    • 1
    • 变量传递
      在组件内定义了相应的变量后,例如组件内部成员变量size,就可以使用this.size方式使用该变量。
    Text('Hello World').fontSize(this.size)
    
    • 1
    • 链式调用
      在配置多个属性时,ArkTS提供了链式调用的方式,通过’.'方式连续配置。
    Text('Hello World')
    .fontSize(this.size)
    .width(100)
    .height(100)
    
    • 1
    • 2
    • 3
    • 4
    • 表达式传递
      属性中还可以传入普通表达式以及三目运算表达式。
    Text('Hello World')
    .fontSize(this.size)
    .width(this.count + 100)
    .height(this.count % 2 === 0 ? 100 : 200)
    
    • 1
    • 2
    • 3
    • 4
    • 内置枚举类型
      除此之外,ArkTS中还提供了内置枚举类型,如Color,FontWeight等,例如设置fontColor改变字体颜色为红色,并私有fontWeight为加粗。
    Text('Hello World')
    .fontSize(this.size)
    .width(this.count + 100)
    .height(this.count % 2 === 0 ? 100 : 200)
    .fontColor(Color.Red)
    .fontWeight(FontWeight.Bold)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    对于有多种组件需要进行组合时,容器组件则是描述了这些组件应该如何排列的结果。

    ArkUI中的布局容器有很多种,在不同的适用场合选择不同的布局容器实现,ArkTS使用容器组件采用花括号语法,内部放置UI描述。
    在这里插入图片描述

    这里介绍最基础的两个布局——列布局和行布局。
    对于如下每一项的布局,两个元素为横向排列,选择Row布局
    Row布局
    在这里插入图片描述

    Row() {
      Image($r('app.media.ic_default'))
      ...
      Text(this.content)
      ...
    }
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    类似下图所示的布局,整体都是从上往下纵向排列,适用的布局方式是Column列布局。
    Column布局
    在这里插入图片描述

    Column() {
      Text($r('app.string.page_title'))
      ...
      ForEach(this.totalTasks,(item) =]]> {
        TodoItem({content:item})
      },...)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4. 改变组件状态

    实际开发中由于交互,页面的内容可能需要产生变化,以每一个ToDoItem为例,其在完成时的状态与未完成时的展示效果是不一样的。

    不同状态的视图
    在这里插入图片描述

    声明式UI的特点就是UI是随数据更改而自动刷新的,我们这里定义了一个类型为boolean的变量isComplete,其被@State装饰后,框架内建立了数据和视图之间的绑定,其值的改变影响UI的显示。

    @State isComplete : boolean = false;
    
    • 1

    @State装饰器的作用
    在这里插入图片描述

    用圆圈和对勾这样两个图片,分别来表示该项是否完成,这部分涉及到内容的切换,需要使用条件渲染if / else语法来进行组件的显示与消失,当判断条件为真时,组件为已完成的状态,反之则为未完成。

    if (this.isComplete) {
      Image($r('app.media.ic_ok'))
      .objectFit(ImageFit.Contain)
      .width($r('app.float.checkbox_width'))
      .height($r('app.float.checkbox_width'))
      .margin($r('app.float.checkbox_margin'))
    } else {
      Image($r('app.media.ic_default'))
      .objectFit(ImageFit.Contain)
      .width($r('app.float.checkbox_width'))
      .height($r('app.float.checkbox_width'))
      .margin($r('app.float.checkbox_margin'))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    由于两个Image的实现具有大量重复代码,ArkTS提供了@Builder装饰器,来修饰一个函数,快速生成布局内容,从而可以避免重复的UI描述内容。这里使用@Bulider声明了一个labelIcon的函数,参数为url,对应要传给Image的图片路径。

    @Builder labelIcon(url) {
      Image(url)
      .objectFit(ImageFit.Contain)
      .width($r('app.float.checkbox_width'))
      .height($r('app.float.checkbox_width'))
      .margin($r('app.float.checkbox_margin'))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用时只需要使用this关键字访问@Builder装饰的函数名,即可快速创建布局。

    if (this.isComplete) {
      this.labelIcon($r('app.media.ic_ok'))
    } else {
      this.labelIcon($r('app.media.ic_default'))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    为了让待办项带给用户的体验更符合已完成的效果,给内容的字体也增加了相应的样式变化,这里使用了三目运算符来根据状态变化修改其透明度和文字样式,如opacity是控制透明度,decoration是文字是否有划线。通过isComplete的值来控制其变化。

    Text(this.content)
      ...
      .opacity(this.isComplete ? CommonConstants.OPACITY_COMPLETED : CommonConstants.OPACITY_DEFAULT)
      .decoration({ type: this.isComplete
                  ? TextDecorationType.LineThrough
                  : TextDecorationType.None })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    最后,为了实现与用户交互的效果,在组件上添加了onClick点击事件,当用户点击该待办项时,数据isComplete的更改就能够触发UI的更新。

    @Componentstruct
    ToDoItem {
      @State isComplete : boolean = false;
      @Builder labelIcon(icon) {...}
      ...
      build() {
        Row() {
          if (this.isComplete) {
            this.labelIcon($r('app.media.ic_ok'))
          } else {
            this.labelIcon($r('app.media.ic_default'))
          }
          ...
        }
        ...
        .onClick(() => {
          this.isComplete= !this.isComplete;
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    5. 循环渲染列表数据

    刚刚只是完成了一个ToDoItem组件的开发,当我们有多条待办数据需要显示在页面时,就需要使用到ForEach循环渲染语法。

    例如这里我们有五条待办数据需要展示在页面上。

    total_Tasks: Array<string]]> = ['早起晨练','准备早餐','阅读名著','学习ArkTS','看剧放松']
    
    • 1

    ForEach基本使用中,只需要了解要渲染的数据以及要生成的UI内容两个部分,例如这里要渲染的数组为以上的五条待办事项,要渲染的内容是ToDoItem这个自定义组件,也可以是其他内置组件。
    ForEach基本使用
    在这里插入图片描述

    ToDoItem这个自定义组件中,每一个ToDoItem要显示的文本参数content需要外部传入,参数传递使用花括号的形式,用content接受数组内的内容项item。
    最终完成的代码及其效果如下。

    @Entry
    @Componentstruct
    ToDoList {
      ...
      build() {
        Row() {
          Column() {
            Text(...)
              ...
            ForEach(this.totalTasks,(item) => {
              TodoItem({content:item})
            },...)
          }
          .width('100%')
        }
        .height('100%')
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    ToDoList页面
    在这里插入图片描述

  • 相关阅读:
    hadoop01--Hadoop完全分布式集群搭建
    亚马逊云科技生成式AI技术辅助教学领域,近实时智能应答2D数字人搭建
    Linux学习-内存管理
    【Spring框架学习3】Spring Bean的作用域 及 生命周期
    发布订阅(观察者)模式之Spring源码ApplicationListener解析
    【信号加密】基于傅里叶变换和小波变换对音频水印的嵌入、提取matlab代码
    keil软件中按F12 无法跳转到函数、变量定义处
    java版 支付宝和微信 h5支付
    OceanBase 4.3 列存存储格式和列存索引存储格式
    PD虚拟机(Parallels Desktop)2024mac苹果电脑19免费版下载
  • 原文地址:https://blog.csdn.net/guoxulieying/article/details/133771340