写点什么

鸿蒙 HarmonyOS 实战 -ArkTS 语言(基本语法)

作者:蜀道山
  • 2024-04-13
    湖南
  • 本文字数:9086 字

    阅读完需:约 30 分钟

鸿蒙HarmonyOS实战-ArkTS语言(基本语法)

🚀一、ArkTS 语言基本语法

🔎1.简介

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


ArkTS 语言具有以下特点:


静态类型检查:开发者在编写代码时可以使用类型注解来进行类型检查,从而减少因类型错误而导致的 bug。


异步/同步编程:ArkTS 语言支持基于 Promise 和 async/await 的异步/同步编程方式,能够更好地处理异步操作。


**内置模块:**ArkTS 语言内置了许多常用的模块,如文件系统、网络请求、图形渲染等,使得开发者不必自己编写这些模块。


**兼容性:**ArkTS 语言使用 TypeScript 语法,可以与 JavaScript 代码无缝集成,并且可以编译成 JavaScript 代码来在其他平台上运行。


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



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


ArkTS 和 HTML 的差别:



🔎2.TypeScript 的基础语法

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


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


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



除了上面提到的变量声明、函数定义、类定义、接口定义和枚举类型外,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 {
<details><summary>点击查看代码</summary>
复制代码


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


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


</details>
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。

🔎3.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.自定义组件

@Componentstruct 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@Componentstruct 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@Componentstruct MyComponent {  build() {    // 根节点唯一且必要,必须为容器组件    Row() {      ChildComponent()     }  }}
@Componentstruct ChildComponent { build() { // 根节点唯一且必要,可为非容器组件 Image('test.jpg') }}
复制代码


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


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


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


@Componentstruct 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.etsimport router from '@ohos.router';
@Entry@Componentstruct 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' }); }) }
}}
@Componentstruct 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@Componentstruct 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@Componentstruct Parent {  label: string = 'Hello';  build() {    Column() {      ABuilder(this.label)    }  }}
复制代码


🦋6.2 @BuilderParam 装饰器


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


☀️6.2.1 装饰指向


1、本地初始化 @BuilderParam


@Builder function GlobalBuilder0() {}
@Componentstruct Child { @Builder doNothingBuilder() {};
@BuilderParam aBuilder0: () => void = this.doNothingBuilder; @BuilderParam aBuilder1: () => void = GlobalBuilder0; build(){}}
复制代码


2、初始化子组件 @BuilderParam


@Componentstruct Child {  @BuilderParam aBuilder0: () => void;
build() { Column() { this.aBuilder0() } }}
@Entry@Componentstruct 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)}
@Componentstruct 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@Componentstruct Parent { label: string = 'Parent'
@Builder componentBuilder() { Text(`${this.label}`) }
build() { Column() { this.componentBuilder() Child({ aBuilder0: this.componentBuilder, aBuilder1: GlobalBuilder1 }) } }}
复制代码


2、尾随闭包


// xxx.ets@Componentstruct 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@Componentstruct 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() { ... }
// 在组件内@Componentstruct FancyUse { @Styles fancy() { .height(100) }}
复制代码


2、组件内


@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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%') }}
复制代码


看完三件事❤️

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我四个小忙:

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。

  • 关注公众号 『 蜀道衫 』,不定期分享原创知识。

  • 同时可以期待后续文章 ing🚀

  • 关注后回复【666】扫码即可获取鸿蒙学习资料包


用户头像

蜀道山

关注

欢迎关注作者公众号:【 蜀道衫】 2023-12-29 加入

3年Java后端,5年Android应用开发,精通Java高并发、JVM调优、以及Android开发各种技能。现专研学习鸿蒙HarmonyOS Next操作系统

评论

发布
暂无评论
鸿蒙HarmonyOS实战-ArkTS语言(基本语法)_typescript_蜀道山_InfoQ写作社区