当前位置: 首页 > article >正文

鸿蒙开发基础入门

一、熟悉目录结构

请添加图片描述
在这里插入图片描述

二、ArkTS语法介绍

  • ArkTS是为构建高性能应用设计的编程语言,语法继承TypeScript,并进行了优化,拥有更强的类型约束
  • ArkTS提供了声明式UI范式,符合移动开发的最新趋势
    在这里插入图片描述
  • ArkTS摒弃了部分影响运行时的性能的语法,比如Any。取而代之的是显式类型定义或类型推断
  • ArkTS提供了更强的并发编程能力
  • ArkTS兼容JS/TS三方库

强的类型约束

  • ArkTS要求所有类型在程序运行前都是已知的。避免运行时类型检查。
// ArkTS
const peter: Person = { // 明确类型
  id: "001",  name: "Peter",  age: 10, gender: "male"
}

ArkTS在运行时不允许更改对象布局。

// TypeScript
class Person {
  id: string = ""
  name: string = ""
}

let peter = new Person()
(peter as any).desc = "XX"
delete (peter as any).desc
// ArkTS
class Person {
  id: string = ""
  name: string = ""
  desc?: string
}

let peter = new Person()
peter.desc = "XX" // 可选属性赋值
peter.desc = undefined // 置空可选属性

声明式UI

  • UI描述:装饰器、自定义组件和UI描述机制
  • 状态管理:数据驱动UI,数据可在组件、页面、应用及跨设备传递

在这里插入图片描述

变量

// 声明变量
let count: number = 0
count = 40

// 声明常量
const MAX_COUNT: number = 100
MAX_COUNT = 200 // 报错,不能修改const声明的值

类型

基本类型:string,number,boolean,enum

引用类型:Array,自定义类

联合类型:Union

类型别名:Aliases

基本类型 string,number,boolean,enum
let name: string = "ZhangSan"
let age: number = 20
let isMale: boolean = true

enum Color {
  Red,
  Blue,
  Green
}

let bgColor: Color = Color.Red
引用类型:Array,自定义类
let students: Array<string> = ["ZhangSan", "LiSi", "WangWu"]
let students: string[] = ["ZhangSan", "LiSi", "WangWu"]

class Person {}
let person: Person = new Person()
联合类型:Union 允许变量的值为多个类型
let id: number | string = 1
id = "001"
类型别名:Aliases 允许给一个类型取别名,方便理解和复用
type Matrix = number[ ][ ]
type NullableObject = Object | null

空安全

基于联合类型,可以声明可空变量。

let name: string | null = null
console.log(name.length.toString()) // 报错,变量可能为null,无法获取null的length

但以上声明使用时不加判断,会报错,因为编译器认为name有可能为null。null无法调用length属性。

if (name != null) {} // 1. 用if/else判断
const unwrapped = name ?? "" // 2. 空值合并表达式,??左边为空时返回??右边的值
let len = name?.length // 3. ?可选链,如果name为null,运算符返回undefined

有多种方法使用可空变量,if/else判断、空值合并表达式或者可选链。

类型判断与类型推断

ArkTS是类型安全的语言,编译器会进行类型检查

let name: string = "ZhangSan"
name = 20 // 报错,number不能赋值给string类型变量

以上代码中,name已经为string类型,不能赋值number类型。

ArkTS可以省略类型声明,此时会自动推导类型

let age = 20 // age自动推断为number类型

age没有声明类型,编译器自动推导为number型。

语句

条件语句及条件表达式

条件语句:根据条件真值(true或false)执行不同代码块

let score: number = 90
let passed: boolean = false
if (score >= 60) {
  passed = true
} else {
  passed = false
}

条件表达式:条件 ? 为真返回值 : 为假返回值

let score: number = 90
let passed: boolean = score >= 60 ? true : false
循环语句

用于重复执行的语句。有for循环、for…of循环以及while循环

给定一个数组

let students: string[] = ["ZhangSan", "LiSi", "WangWu"]

for循环

for (let i = 0; i < students.length; i++) {
  console.log(students[i])
}

for…of循环

for (let student of students) {
  console.log(student)
}

while循环

let index = 0
while (index < students.length) {
  console.log(students[index])
  index++
}

函数的声明和使用

函数是多条语句的组合,组成一个可重用的代码块。

用function声明函数

格式如下

function 函数名(参数1: 参数类型1, 参数2: 参数类型2): 返回类型 {
// 函数体
}

例:

function printStudentInfo(students: string[]): void {
  for (let student of students) {
    console.log(student)
  }
}

printStudentInfo(["ZhangSan", "LiSi", "WangWu"])

箭头函数/lambda

简化声明,匿名函数。通常用于把函数作为参数传递。格式如下

(参数1: 参数类型1, 参数2: 参数类型2): 返回类型 => { // 函数体 }

例:

(name: string): void => { console.log(name) }

上例中,返回类型void可以省略。如果函数体只有一行、整个函数声明只有一行,可以省略花括号。

闭包函数

一个函数可以作为另一个函数的返回值。

type returnType = () => string
function outerFunc(): () => string {
  let count = 0
  return (): string => {
    count++
    return count.toString()
  }
}

let invoker = outerFunc()
console.log(invoker()) // 输出1
console.log(invoker()) // 输出2

类的声明和使用

ArkTS支持面向对象编程。可声明对象属性和方法。

class Person {
  name: string = "ZhangSan"
  age: number = 20
  isMale: boolean = true
}

创建类的实例

const person = new Person()
console.log(person.name)

const person: Person = {
  name: "ZhangSan",
  age: 29,
  isMale: true
}
console.log(person.name)

构造器:在创建类实例的时候可以执行一些代码或传参。

class Person {
  name: string = "ZhangSan"
  age: number = 20
  isMale: boolean = true
  constructor(
    name: string,
    age: number,
    isMale: boolean
  ) {
    this.name = name
    this.age = age
    this.isMale = isMale
  }
}

const person = new Person("ZhangSan", 20, false)
console.log(person.name)

上例中在Person声明时定义了一个constructor构造器,创建实例的时候传入相应参数,会执行构造器里的语句。

实例方法:实例调用的方法

class Person {
  name: string = "ZhangSan"
  age: number = 20
  isMale: boolean = true
  
  constructor(...){...}
  
  printInfo() {
    if (this.isMale) {
      console.log(`${this.name} is a boy, he is ${this.age} years old`)
    } else {
      console.log(`${this.name} is a girl, she is ${this.age} years old`)
    }
  }
}

const person = new Person("LiSi", 18, false)
person.printInfo()

上例中声明了一个实例方法printInfo()。在实例person中被调用。

可见性及属性

类成员有 public、private、protected可见性。
public修饰符表示这是公开成员,外部可以自由访问。
private修饰符表示私有成员,只能用在当前类的内部,类的实例和子类都不能使用该成员。
protected修饰符表示该成员是保护成员,只能在类的内部使用该成员,实例无法使用该成员,但是子类内部可以使用。

class Person {
  public name: string = "ZhangSan"
  private _age: number = 20
  isMale: boolean = true

  constructor(...) {...}

  printInfo() {...}

  get age(): number {
    return this._age
  }
  set age(age: number) {
    this._age = age
  }
}

const person: Person = new Person("LiSi", 18, true)
console.log(person._age.toString()) // 无法访问
console.log(person.age.toString())

另外,对于上例中private _age可以声明setter和getter,在调用的时候可以与访问属性的语法一样来访问。

继承

子类继承父类的特征和行为。关键字extends。

class Employee extends Person {
  department: string
  
  constructor(
    name: string,
    age: number,
    isMale: boolean,
    department: string
  ) {
    super(name, age, isMale)
    this.department = department
  }
}

const employee: Employee = new Employee("LiSi", 18, false, "XCompany")
employee.printInfo()

上例中的Employee继承了Person。增加了department属性。构造函数重写了父类的构造函数。使用super()方法可以调用父类的方法实现。

多态

子类继承父类,可以重写父类方法

class Employee extends Person {
  department: string

  constructor(...) {...}

  printInfo(): void {
    super.printInfo()
    console.log(`working in ${this.department}}`)
  }
}

const person: Person = new Person("LiSi", 18, false)
person.printInfo()

const employee: Employee = new Employee("LiSi", 18, false, "XCompany")
employee.printInfo()

Employee构造函数重写了父类的构造函数。使用super()方法可以调用父类的方法实现。在Employee实例化的时候调用重写的构造函数。另外printInfo()也重写了父类的对应方法,并用super()调用了父类的方法。在调用Employee的printInfo()方法时会先调用父类的方法,再执行之后的代码。

模块导出与导入

ArkTS中文件内的作用域是独立的。如果想要在B文件中引用A文件定义的变量、函数、类等,需要使用export关键字。

// Person.ets
export class Person {...}
// Index.ets
import { Person } from './Person'

const person = new Person("LiSi", 18, false)
person.printInfo()

声明式UI

声明式UI是当前移动开发的UI语法新趋势。它相较于传统的命令式UI,代码结构更直观。拥有声明式布局描述及状态驱动视图更新的特点。

传统的命令式UI代码,我们拿安卓举例,像这样

TextView title = findViewById(R.id.tv_title);
title.setText("HarmonyOS Developer World") ;
Button button = findViewById(R.id.btn_join);
Button.setText("Join Now");

LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(300,300);
child.setLayoutParams(layoutParams);
linear_layout.addView(title, Integer.valueOf(add_index) - 1);
linear_layout.addView(button, Integer.valueOf(add_index) - 1);

而如果使用声明式UI,可以写成这样

Column() { 
	Text("HarmonyOS Developer World") 
	Button("Join Now") 
}

本章将以一个待办事项界面为例讲解ArkTS声明式UI。

image-20240418222343655

声明式描述

布局描述

下例中描述了一个横向布局,里面依次排列了一张图片和一个文本框,对应的UI组件如图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

属性设置

使用点语法设置组件属性

Text("待办")
  .fontSize(28)
  .fontWeight(FontWeight.Bold)

渲染控制

支持if、else条件渲染。

Column() {
  Text(`count=${this.count}`)
  if (this.count > 0) {
    Text(`count is positive`)
      .fontColor(Color.Green)
  }
}

支持ForEach循环

Column() {
  ForEach(this.simpleList, (item: string) => {
    Text(item)
  }, (item: string) => item)
}

支持LazyForEach循环

private data: MyDataSource = new MyDataSource()List({ space: 3 }) {
  LazyForEach(this.data, (item: string) => {
    ListItem() {
      Row() {
        Text(item)
         .fontSize(50)
      }
    }
  }, (item: string) => item)
}.cachedCount(5)

class MyDataSource extends BasicDataSource {
  private dataArray: string[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): string {
    return this.dataArray[index];
  }

  public addData(index: number, data: string): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: string): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

// Basic implementation of IDataSource to handle data listener
class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: string[] = [];

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): string {
    return this.originDataArray[index];
  }

  // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  // 通知LazyForEach组件需要重载所有子组件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知LazyForEach组件需要在index对应索引处添加子组件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  // 通知LazyForEach组件需要在index对应索引处删除该子组件
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }

  // 通知LazyForEach组件将from索引和to索引处的子组件进行交换
  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    })
  }
}

状态驱动UI更新

使用@State @Prop @Link等驱动UI更新。

下例说明了一个状态属性是如何驱动UI更新的,isComplete选择不同的图片资源。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下图说明了isComplete控制了文字的样式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以下是其他状态装饰器

•@State:@State装饰的变量拥有其所属组件的状态,可以作为其子组件单向和双向同步的数据源。当其数值改变时,会引起相关组件的渲染刷新。

•@Prop:@Prop装饰的变量可以和父组件建立单向同步关系,@Prop装饰的变量是可变的,但修改不会同步回父组件。

•@Link:@Link装饰的变量和父组件构建双向同步关系的状态变量,父组件会接受来自@Link装饰的变量的修改的同步,父组件的更新也会同步给@Link装饰的变量。

•@Provide/@Consume:@Provide/@Consume装饰的变量用于跨组件层级(多层组件)同步状态变量,可以不需要通过参数命名机制传递,通过alias(别名)或者属性名绑定。

•@Observed:@Observed装饰class,需要观察多层嵌套场景的class需要被@Observed装饰。单独使用@Observed没有任何作用,需要和@ObjectLink、@Prop连用。

•@ObjectLink:@ObjectLink装饰的变量接收@Observed装饰的class的实例,应用于观察多层嵌套场景,和父组件的数据源构建双向同步。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自定义组件

当界面中有相同结构的模块时,可以封装成一个独立组件。

比如每一行代办事项都是由一张图片和一个文本组成。

image-20240418222343655

自定义组件用@Component装饰器来声明。ToDoItem表示每一行代办事项。

@Component
export default struct ToDoItem {}

在首页如图所示来使用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

子组件的数据

复用的子组件,需要传入数据来显示不同内容,本例中通过content属性给ToDoItem传递数据。

@Component
export default struct ToDoItem {
  private content?: string
  build() {
    Row() {
      Image($r('app.media.ic_default'))
      Text(this.content)
    }
  }
}

在首页中参考如下代码传递数据

@Entry
@Component
struct ToDoListPage {
  build() {
    Column({ space: 16 }) {
      Text(“待办”)ToDoItem({ content: "学习" })
    }
  }
}

组件的生命周期

每个组件,包括页面组件和子组件,都有相应的生命周期。生命周期如下图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自定义组件和页面的关系:

•自定义组件:@Component装饰的UI单元,可以组合多个系统组件实现UI的复用,可以调用组件的生命周期。

•页面:即应用的UI页面。可以由一个或者多个自定义组件组成,@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry。只有被@Entry装饰的组件才可以调用页面的生命周期。

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

•onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。

•onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。

•onBackPress:当用户点击返回按钮时触发。

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

•aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。

•aboutToDisappear:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。

生命周期流程如下图所示,下图展示的是被@Entry装饰的组件(页面)生命周期。

@Builder装饰器:自定义构建函数

区别与子组件的声明,在当前组件中也可以使用@Builder来装饰一个方法,用于提取一些可复用的UI代码。

在ToDoItem中,对于Image组件的描述可以提取成一个方法,并用@Builder来装饰,然后可以根据条件引用不同图片文件。

@Component
export default struct ToDoItem {
  private content?: string

  @Builder
  labelIcon(icon: Resource) {
    Image(icon)
      .objectFit(ImageFit.Contain)
      .width(28)
      .height(28)
      .margin(20)
  }
  build() {
    Row() {
      this.labelIcon($r('app.media.ic_default'))
      Text()
    }
  }
}

渲染列表数据

子组件ToDoItem的设计已经完成,现在在首页,我们需要用一个属性提供列表数据。下例中totalTasks保存了一个字符串数组。用ForEach来循环渲染。

@Entry
@Component
struct ToDoListPage {
  private totalTasks: Array<string> = [
    "晨练",
    "做早餐",
    "读书",
    "学习",
    "刷剧"
  ]

  build() {
    Column({ space: 16 }) {
      Text("待办")ForEach(this.totalTasks, (item: string) => {
        ToDoItem({ content: item })
      },)
    }
  }
}

http://www.kler.cn/a/389036.html

相关文章:

  • 实现 MVC 模式
  • 【go从零单排】Timer、Epoch 时间函数
  • 2024开发者浏览器必备扩展,不允许还有人不知道~
  • Debezium日常分享系列之:异步 Debezium 嵌入式引擎
  • 微服务架构面试内容整理-SpringCloud Netflix‌与Spring Cloud Alibaba比较
  • ESLint 使用教程(七):ESLint还能校验JSON文件内容?
  • BP 神经网络学习 MATLAB 函数详解及应用
  • 【Istio】Istio原理
  • web安全漏洞之文件上传
  • 【JWT】Asp.Net Core中JWT刷新Token解决方案
  • DCN DCWS-6028神州数码 AC 设备配置笔记
  • ESLint 使用教程(三):12个ESLint 配置项功能与使用方式详解
  • SDL渲染器和纹理
  • 2024-11-10-leetcode每日一题-540. 有序数组中的单一元素
  • Python数据分析-Google Play商店应用数据分析
  • C#里对数组的排序操作
  • 关于我重生到21世纪学C语言这件事——函数详解
  • 初始JavaEE篇——多线程(8):JUC的组件
  • Python 获取PDF的各种页面信息(页数、页面尺寸、旋转角度、页面方向等)
  • jupyter添加、删除、查看内核
  • 一篇Spring Boot 笔记
  • Python | Leetcode Python题解之第542题01矩阵
  • 【Ubuntu20】VSCode Python代码规范工具配置 Pylint + Black + MyPy + isort
  • 微信小程序运营日记(第四天)
  • 16.UE5拉怪机制,怪物攻击玩家,伤害源,修复原视频中的BUG
  • VAE的原理及MNIST数据生成