鸿蒙HarmonyOS实战-ArkTS语言基本语法讲解

目录
文章目录隐藏
  1. 一、ArkTS 语言基本语法

一、ArkTS 语言基本语法

1.简介

HarmonyOS 的 ArkTS 语言是一种基于 TypeScript 开发的语言,它专为 HarmonyOS 系统开发而设计。ArkTS 语言结合了 JavaScript 的灵活性和 TypeScript 的严谨性,使得开发者能够快速、高效地开发出高质量的 HarmonyOS 应用程序。

ArkTS 语言具有以下特点:

  1. 静态类型检查:开发者在编写代码时可以使用类型注解来进行类型检查,从而减少因类型错误而导致的 bug。
  2. 异步/同步编程:ArkTS 语言支持基于 Promise 和 async/await 的异步/同步编程方式,能够更好地处理异步操作。
  3. 内置模块:ArkTS 语言内置了许多常用的模块,如文件系统、网络请求、图形渲染等,使得开发者不必自己编写这些模块。
  4. 兼容性:ArkTS 语言使用 TypeScript 语法,可以与 JavaScript 代码无缝集成,并且可以编译成 JavaScript 代码来在其他平台上运行。

ArkTS 语言基础类库是 HarmonyOS 系统上为应用开发者提供的常用基础能力,主要包含能力如下图所示:

ArkTS 语言基础类库

ArkTS 是 HarmonyOS 优选的主力应用开发语言。ArkTS 围绕应用开发在 TypeScript(简称 TS)生态基础上做了进一步扩展,继承了 TS 的所有特性,是 TS 的超集。

ArkTS 和 HTML 的差别:

ArkTS 和 HTML 的差别

ArkTS 和 HTML 的差别

2.TypeScript 的基础语法

TypeScript 是一种由微软开发的 JavaScript 超集语言,它支持 JavaScript 的所有语法,但添加了一些新的特性和语法,使开发更加可靠和高效。TypeScript 最大的特点是引入了静态类型,开发者可以在编译时发现类型错误,提高代码的可维护性和可读性。

TypeScript 代码可以在编译时被转换成 JavaScript 代码,在浏览器和 Node.js 环境下都可以运行。虽然 TypeScript 相对于 JavaScript 来说更加复杂,但是它可以帮助开发者更好地组织和管理复杂的项目,特别是在团队协作中提高代码的质量和可维护性。

TypeScript 基础知识包括基本类型、变量声明、函数、类、接口、泛型等。另外,TypeScript 还支持模块化开发,可以使用 ES 模块规范或者 CommonJS 规范导入和导出模块。在实际项目开发中,TypeScript 还可以结合工具链如 Webpack、Babel 进行编译、打包等操作。

TypeScript 的基础语法

除了上面提到的变量声明、函数定义、类定义、接口定义和枚举类型外,TypeScript 还有一些基础语法需要掌握:

2.1 类型注解

TypeScript 的静态类型检查是通过类型注解实现的。在声明变量或函数时,可以使用冒号加上类型注解,指定变量或函数的类型。例如:

let name: string = "TypeScript";

function add(a: number, b: number): number {
    return a + b;
}

2.2 接口

TypeScript 的接口是用来描述对象的形状的。可以定义对象需要包含哪些属性和方法,以及它们的类型。例如:

interface Person {
    name: string;
    age: number;
    sayHello(): void;
}

let tom: Person = {
    name: "Tom",
    age: 18,
    sayHello: function() {
        console.log(`Hello, my name is ${this.name}!`);
    }
};

2.3 泛型

TypeScript 的泛型可以帮助我们编写更加灵活、可重用的代码。它允许在编写函数、类或接口时使用参数化类型,从而提高代码的通用性和可读性。

例如:

function identity<T>(arg: T): T {
    return arg;
}

let output = identity<string>("TypeScript");
console.log(output); // 输出 TypeScript

2.4 类的继承

TypeScript 的类可以继承其他类,从而实现代码的重用和扩展。通过关键字 extends 可以让一个类继承另一个类,并继承其属性和方法。例如:

class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    move(distance: number = 0) {
        console.log(`${this.name} moved ${distance}m.`);
    }
}

class Dog extends Animal {
    bark() {
        console.log("Woof! Woof!");
    }
}

let dog = new Dog("Bobby");
dog.move(10); // 输出 "Bobby moved 10m."
dog.bark(); // 输出 "Woof! Woof!"

2.5 类的访问修饰符

TypeScript 的类可以通过访问修饰符来控制类的属性和方法的访问权限。有三个访问修饰符可以使用:public、private 和 protected。默认情况下,都是 public。

  • public:公共的,任何外部或内部都可以访问。
  • private:私有的,只有类的内部可以访问,外部无法访问。
  • protected:受保护的,只有类的内部和其子类可以访问,外部无法访问。
class Person {
    protected name: string;
    constructor(name: string) {
        this.name = name;
    }
    protected sayHello() {
        console.log(`Hello, I'm ${this.name}.`);
    }
}

class Student extends Person {
    constructor(name: string) {
        super(name);
    }
    public sayHelloToTeacher(teacher: Person) {
        console.log(`Hello, ${teacher.name}, I'm ${this.name}.`);
    }
}

let tom = new Student("Tom");
let bob = new Person("Bob");
tom.sayHelloToTeacher(bob); // 输出 "Hello, Bob, I'm Tom."
bob.sayHello(); // 报错:属性 'sayHello' 受保护,只能在类 'Person' 及其子类中访问。

以上只是举例一些 TS 的基础语法,TS 内容远不止这些,不懂的可以去学学 TS。

推荐大家看我之前的 TypeScript 教程学习:TypeScript 教程

3. ArkTS 的基本组成

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 的声明式开发范式。

除此之外,ArkTS 扩展了多种语法范式来使开发更加便捷:

  • @Builder/@BuilderParam:特殊的封装 UI 描述的方法,细粒度的封装和复用 UI 描述。
  • @Extend/@Style:扩展内置组件和封装属性样式,更灵活地组合内置组件。
  • stateStyles:多态样式,可以依据组件的内部状态的不同,设置不同样式。

4.自定义组件

@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!';
        })
    }
  }
}

@Entry
@Component
struct ParentComponent {
  build() {
    Column() {
      Text('ArkUI message')
      HelloComponent({ message: 'Hello, World!' });
      Divider()
      HelloComponent({ message: '你好!' });
    }
  }
}
  • struct:自定义组件基于 struct 实现,struct + 自定义组件名 +
    {…}的组合构成自定义组件,不能有继承关系。对于 struct 的实例化,可以省略 new。
  • build()函数:build()函数用于定义自定义组件的声明式 UI 描述,自定义组件必须定义 build()函数。
  • @Entry:@Entry装饰的自定义组件将作为 UI 页面的入口。在单个 UI 页面中,最多可以使用@Entry 装饰一个自定义组件。@Entry 可以接受一个可选的 LocalStorage 的参数。

4.1 build()函数规范

1、根节点唯一

@Entry
@Component
struct MyComponent {
  build() {
    // 根节点唯一且必要,必须为容器组件
    Row() {
      ChildComponent() 
    }
  }
}

@Component
struct ChildComponent {
  build() {
    // 根节点唯一且必要,可为非容器组件
    Image('test.jpg')
  }
}

2、不允许声明本地变量、打印、作用域

build() {
  // 反例:不允许声明本地变量
  let a: number = 1;
  // 反例:不允许 console.info
  console.info('print debug log');
  // 反例:不允许本地作用域
  {
    ...
  }
}

3、不允许调用没有用@Builder 装饰的方法,允许系统组件的参数是 TS 方法的返回值。

@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())
    }
  }
}

4、不允许 switch 和表达式

build() {
  Column() {
    // 反例:不允许使用 switch 语法
    switch (expression) {
      case 1:
        Text('...')
        break;
      case 2:
        Image('...')
        break;
      default:
        Text('...')
        break;
    }
    // 反例:不允许使用表达式
    (this.aVar > 10) ? Text('...') : Image('...')
  }
}

5.页面和自定义组件生命周期

页面和自定义组件生命周期

页面生命周期,即被@Entry 装饰的组件生命周期,提供以下生命周期接口:

  • onPageShow:页面每次显示时触发。
  • onPageHide:页面每次隐藏时触发一次。
  • onBackPress:当用户点击返回按钮时触发。

组件生命周期,即一般用@Component 装饰的自定义组件的生命周期,提供以下生命周期接口:

  • aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其 build()函数之前执行。
  • aboutToDisappear:在自定义组件即将析构销毁时执行。
// Index.ets
import router from '@ohos.router';

@Entry
@Component
struct MyComponent {
  @State showChild: boolean = true;

  // 只有被@Entry 装饰的组件才可以调用页面的生命周期
  onPageShow() {
    console.info('Index onPageShow');
  }
  // 只有被@Entry 装饰的组件才可以调用页面的生命周期
  onPageHide() {
    console.info('Index onPageHide');
  }

  // 只有被@Entry 装饰的组件才可以调用页面的生命周期
  onBackPress() {
    console.info('Index onBackPress');
  }

  // 组件生命周期
  aboutToAppear() {
    console.info('MyComponent aboutToAppear');
  }

  // 组件生命周期
  aboutToDisappear() {
    console.info('MyComponent aboutToDisappear');
  }

  build() {
    Column() {
      // this.showChild 为 true,创建 Child 子组件,执行 Child aboutToAppear
      if (this.showChild) {
        Child()
      }
      // this.showChild 为 false,删除 Child 子组件,执行 Child aboutToDisappear
      Button('create or delete Child').onClick(() => {
        this.showChild = false;
      })
      // push 到 Page2 页面,执行 onPageHide
      Button('push to next page')
        .onClick(() => {
          router.pushUrl({ url: 'pages/Page2' });
        })
    }

  }
}

@Component
struct Child {
  @State title: string = 'Hello World';
  // 组件生命周期
  aboutToDisappear() {
    console.info('[lifeCycle] Child aboutToDisappear')
  }
  // 组件生命周期
  aboutToAppear() {
    console.info('[lifeCycle] Child aboutToAppear')
  }

  build() {
    Text(this.title).fontSize(50).onClick(() => {
      this.title = 'Hello ArkUI';
    })
  }
}

6.装饰函数

6.1 @Builder 装饰器

@Builder 主要是定义页面 UI
6.1.1 装饰指向
1、自定义组件内自定义构建函数

@Builder MyBuilderFunction(){ ... }
#使用
this.MyBuilderFunction(){ ... }

2、MyGlobalBuilderFunction()

@Builder function MyGlobalBuilderFunction(){ ... }
#使用
MyGlobalBuilderFunction()

6.1.2 参数传递

1、按引用传递参数

@Builder function ABuilder($$: { paramA1: string }) {
  Row() {
    Text(`UseStateVarByReference: ${$$.paramA1} `)
  }
}
@Entry
@Component
struct Parent {
  @State label: string = 'Hello';
  build() {
    Column() {
      // 在 Parent 组件中调用 ABuilder 的时候,将 this.label 引用传递给 ABuilder
      ABuilder({ paramA1: this.label })
      Button('Click me').onClick(() => {
        // 点击“Click me”后,UI 从“Hello”刷新为“ArkUI”
        this.label = 'ArkUI';
      })
    }
  }
}

2、按值传递参数

@Builder function ABuilder(paramA1: string) {
  Row() {
    Text(`UseStateVarByValue: ${paramA1} `)
  }
}
@Entry
@Component
struct Parent {
  label: string = 'Hello';
  build() {
    Column() {
      ABuilder(this.label)
    }
  }
}

6.2 @BuilderParam 装饰器

@BuilderParam用来装饰指向@Builder方法的变量,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的功能。

6.2.1 装饰指向

1、本地初始化@BuilderParam

@Builder function GlobalBuilder0() {}

@Component
struct Child {
  @Builder doNothingBuilder() {};

  @BuilderParam aBuilder0: () => void = this.doNothingBuilder;
  @BuilderParam aBuilder1: () => void = GlobalBuilder0;
  build(){}
}

2、初始化子组件@BuilderParam

@Component
struct Child {
  @BuilderParam aBuilder0: () => void;

  build() {
    Column() {
      this.aBuilder0()
    }
  }
}

@Entry
@Component
struct Parent {
  @Builder componentBuilder() {
    Text(`Parent builder `)
  }

  build() {
    Column() {
      Child({ aBuilder0: this.componentBuilder })
    }
  }
}

this 都是器其本身,不会存在传递。

6.2.2 使用场景

1、参数化传递

@Builder function GlobalBuilder1($$ : {label: string }) {
  Text($$.label)
    .width(400)
    .height(50)
    .backgroundColor(Color.Blue)
}

@Component
struct Child {
  label: string = 'Child'
  // 无参数类,指向的 componentBuilder 也是无参数类型
  @BuilderParam aBuilder0: () => void;
  // 有参数类型,指向的 GlobalBuilder1 也是有参数类型的方法
  @BuilderParam aBuilder1: ($$ : { label : string}) => void;

  build() {
    Column() {
      this.aBuilder0()
      this.aBuilder1({label: 'global Builder label' } )
    }
  }
}

@Entry
@Component
struct Parent {
  label: string = 'Parent'

  @Builder componentBuilder() {
    Text(`${this.label}`)
  }

  build() {
    Column() {
      this.componentBuilder()
      Child({ aBuilder0: this.componentBuilder, aBuilder1: GlobalBuilder1 })
    }
  }
}

2、尾随闭包

// xxx.ets
@Component
struct CustomContainer {
  @Prop header: string;
  @BuilderParam closer: () => void

  build() {
    Column() {
      Text(this.header)
        .fontSize(30)
      this.closer()
    }
  }
}

@Builder function specificParam(label1: string, label2: string) {
  Column() {
    Text(label1)
      .fontSize(30)
    Text(label2)
      .fontSize(30)
  }
}

@Entry
@Component
struct CustomContainerUser {
  @State text: string = 'header';

  build() {
    Column() {
      // 创建 CustomContainer,在创建 CustomContainer 时,通过其后紧跟一个大括号“{}”形成尾随闭包
      // 作为传递给子组件 CustomContainer @BuilderParam closer: () => void 的参数
      CustomContainer({ header: this.text }) {
        Column() {
          specificParam('testA', 'testB')
        }.backgroundColor(Color.Yellow)
        .onClick(() => {
          this.text = 'changeHeader';
        })
      }
    }
  }
}

6.3 @Styles 装饰器

@Styles装饰器主要是定义公共样式

6.3.1 装饰指向

1、全局

// 全局
@Styles function functionName() { ... }

// 在组件内
@Component
struct FancyUse {
  @Styles fancy() {
    .height(100)
  }
}

2、组件内

@Component
struct FancyUse {
  @State heightValue: number = 100
  @Styles fancy() {
    .height(this.heightValue)
    .backgroundColor(Color.Yellow)
    .onClick(() => {
      this.heightValue = 200
    })
  }
}

6.3.2 使用场景

// 定义在全局的@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 ()
        .fontSize(30)
      // 使用组件内的@Styles 封装的样式
      Text('FancyB')
        .fancy()
        .fontSize(30)
    }
  }
}

6.4 @Extend 装饰器

@Extend 用于扩展原生组件样式,作用和@Styles 差不多。

6.4.1 装饰指向

@Extend 仅支持定义在全局,不支持在组件内部定义

1、@Extend 支持封装指定的组件的私有属性和私有事件

// @Extend(Text)可以支持 Text 的私有属性 fontColor
@Extend(Text) function fancy () {
  .fontColor(Color.Red)
}
// superFancyText 可以调用预定义的 fancy
@Extend(Text) function superFancyText(size:number) {
    .fontSize(size)
    .fancy()
}

2、@Extend 装饰的方法支持参数

// xxx.ets
@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)
    }
  }
}

3、@Extend 装饰的方法的参数可以为 function

@Extend(Text) function makeMeClick(onClick: () => void) {
  .backgroundColor(Color.Blue)
  .onClick(onClick)
}

@Entry
@Component
struct FancyUse {
  @State label: string = 'Hello World';

  onClickHandler() {
    this.label = 'Hello ArkUI';
  }

  build() {
    Row({ space: 10 }) {
      Text(`${this.label}`)
        .makeMeClick(this.onClickHandler.bind(this))
    }
  }
}

4、@Extend 的参数可以为状态变量

@Extend(Text) function fancy (fontSize: number) {
  .fontColor(Color.Red)
  .fontSize(fontSize)
}

@Entry
@Component
struct FancyUse {
  @State fontSizeValue: number = 20
  build() {
    Row({ space: 10 }) {
      Text('Fancy')
        .fancy(this.fontSizeValue)
        .onClick(() => {
          this.fontSizeValue = 30
        })
    }
  }
}

6.4.2 使用场景

@Extend(Text) function fancyText(weightValue: number, color: Color) {
  .fontStyle(FontStyle.Italic)
  .fontWeight(weightValue)
  .backgroundColor(color)
}

@Entry
@Component
struct FancyUse {
  @State label: string = 'Hello World'

  build() {
    Row({ space: 10 }) {
      Text(`${this.label}`)
        .fancyText(100, Color.Blue)
      Text(`${this.label}`)
        .fancyText(200, Color.Pink)
      Text(`${this.label}`)
        .fancyText(300, Color.Orange)
    }.margin('20%')
  }
}

7.多态样式

stateStyles 是属性方法,可以根据 UI 内部状态来设置样式,类似于 css 伪类,但语法不同。ArkUI 提供以下四种状态:

  • focused:获焦态
  • normal:正常态
  • pressed:按压态
  • disabled:不可用态

7.1 基本使用

@Entry
@Component
struct CompWithInlineStateStyles {
  @State focusedColor: Color = Color.Red;
  normalColor: Color = Color.Green
  build() {
    Column() {
      Button('clickMe').height(100).width(100)
        .stateStyles({
          normal: {
            .backgroundColor(this.normalColor)
          },
          focused: {
            .backgroundColor(this.focusedColor)
          }
        })
        .onClick(() => {
          this.focusedColor = Color.Pink
        })
        .margin('30%')
    }
  }
}

7.2 @Styles 和 stateStyles 联合使用

@Entry
@Component
struct MyComponent {
  @Styles normalStyle() {
    .backgroundColor(Color.Gray)
  }

  @Styles pressedStyle() {
    .backgroundColor(Color.Red)
  }

  build() {
    Column() {
      Text('Text1')
        .fontSize(50)
        .fontColor(Color.White)
        .stateStyles({
          normal: this.normalStyle,
          pressed: this.pressedStyle,
        })
    }
  }
}

7.3 stateStyles 里使用常规变量和状态变量

@Entry
@Component
struct CompWithInlineStateStyles {
  @State focusedColor: Color = Color.Red;
  normalColor: Color = Color.Green

  build() {
    Button('clickMe').height(100).width(100)
      .stateStyles({
        normal: {
          .backgroundColor(this.normalColor)
        },
        focused: {
          .backgroundColor(this.focusedColor)
        }
      })
      .onClick(() => {
        this.focusedColor = Color.Pink
      })
      .margin('30%')
  }
}

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系maynote@foxmail.com处理
码云笔记 » 鸿蒙HarmonyOS实战-ArkTS语言基本语法讲解

发表回复