欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > 【Harmony OS 4.0】从零开始,快速上手

【Harmony OS 4.0】从零开始,快速上手

2025/2/5 4:04:30 来源:https://blog.csdn.net/weixin_43900414/article/details/141161046  浏览:    关键词:【Harmony OS 4.0】从零开始,快速上手

2019年8月份Harmony OS 1.0,
2020年9月份Harmony OS 2.0
2022年7月份Harmony OS 3.0
2023年3月份Harmony OS 4.0,不兼容 android app

1. 快速上手

1.1 下载并安装 DevEco Studio

1.2 创建项目并初始化

  1. 项目 build init 时报错:request to http://registry.cnpmjs.org/pnpm failed, reason: getaddrinfo ENOTFOUND registry.cnpmjs.org
  2. 运行 npm install -g cnpm --registry=https://registry.npm.taobao.org,仍报错:request to https://registry.npm.taobao.org/cnpm failed, reason: certificate has expired. 显示证书过期。使用的是老版本的淘宝镜像,淘宝不在维护.
  3. 设置淘宝最新的镜像源. 运行 npm install -g cnpm --registry=https://registry.npmmirror.com. 成功

1.3 创建模拟器

  1. 创建模拟器前需要先申请参加模拟器Beta活动.
  2. 若提示该账号没有权限,请先点击“Submit the application form”完成权限申请.

1.4 了解项目目录:工程级目录 / 模块级目录

  1. 通过修改下面俩个文件路径设置模拟器打开的主页面
    1.1 src/main/ets/entryability/entryability.ts ⇒ windowStage.loadContent(‘test_pages/test2’)
    1.2 resources/base/profile/main_pages.json ⇒ { “src”: [‘test_pages/test2’] },配置router路径

1.5 DevEco Studio 配置 TypeScript

  1. 安装 Nodejs,配置 Nodejs 的环境变量
  2. 安装 typescript,npm install -g typescript,tsc -v 查看版本号
  3. 编译 .ts 文件的命令:tsc 文件名
    编译 .js 文件的命令:node 文件名

2. TypeScript 快速入门

2.1 掌握 ts 常用基本类型

// 1. boolean 类型
let isOff = true
let isOn = false// 2. 数字类型
let a: Number/number = 2// 3. 字符串类型
let aStr:string = "hello"// 4. 俩种数组类型
let arr1:number[] = [1, 3, 5]
let arr2: Array<string> = ["hello", "world"]// 4.1 回顾splice用法,改变原数组,返回修改后的数组
let arr3 = [1, 3, 5, 7]
arr3.splice(2, 0, 3) // 把值3添加到下标2的地方
console.log(arr3) // [1, 3, 3, 5, 7]
arr3.splice(4, 1) // 删除下标4的值
console.log(arr3) // [1, 3, 3, 5]// 5. 定义一个元组类型  ==>  允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let arr4: [string, number]
arr4 = ['hello', 100]
arr4 = [100, 'hello'] // error demo
arr4 = ['hello', 100, 200] // error demo// 6. 定义枚举 enum  ==>  是对javascript标准数据类型的一个补充,可以为一组数值赋予友好的名字。
enum Color { red, green, blue }
let c: Color = Color.green// 7. 定义未知类型 any/Unknown  ==>  不清楚类型的变量,不希望类型检查器对这些值进行检查,直接通过编译阶段的检查。
let noSure: unknown/any
noSure = 4 // 这里4可以是任意值// 8. viod  ==>  函数没有返回值
function myfunc(): void {// 这里不能有return
}// 9. Null  ==> 空值,没有给它分配内存,内存中没有变量;
//    undefined  ==> 未定义的变量,有这个变量,但是没有定义// 10. 联合类型
let a: string | number
a = 'hello'
a = 100

2.2 掌握条件语句

// 1. if
// 2. if...else 
// 3. if...else if...else 
// 4. switch...case,用来做多条件的等于判断
let gender: string = 'mail'
switch (gender) {case 'mail': {console.log('男')break; // 满足条件就终止判断}case 'femail': {console.log('女')break;}default: { // 上面所有case条件都不满足,就会进入defaultconsole.log('不能确定性别')break;}
}

2.3 掌握循环语句

// 1. 
let i: numebr;
for(i = 1;i <= 10;i++) {console.log(i)
}// 2. 只能针对集合或者是列表
let j: any
// let j: unkonwn
let nums: Array<number> = [12, 23, 56, 32]
for (j in nums) {console.log(nums[j])
}

2.4 掌握函数

// 1. 有名函数
function add(x: number, y: number): void{}
add(10, 20)// 2. 匿名函数
let add_Func = function(x: number, y: number): number{return x + y
}
add_Func(10, 20)// 3. 可选参数的函数
function fn(x: number, name?: string) :void {}
fn(10)// 4. 剩余参数 ==> 个数不限的可选参数
function fn(x: number, ...other: Array<any>) :void {}
fn(10, 'hello', 30)// 5. 箭头函数  ==>  定义匿名函数的简写语法
let fn = (x: number, y: number) => {}

2.5 掌握类对象 ==> public(公共的,在任何地方都一用),private(私有的,只能在类里面用), protected(在一个包里面可以用)

2.5.1 在 ArkTs 中,private 好处是类对象外的代码无法访问到,不允许别人在类外面随意改动它
2.5.2 在 ArkTs 中,private 必须本地初始化。

// 1. 定义一个Person类
class Person {// 私有属性private name: stringprivate age: number// 定义构造函数constructor(name: string, age: number){this.name = namethis.age = age}// 公共成员函数public getPersonInfo() :string {return 	`我的名字是${this.name},年龄是${this.age}`}
}
let p = new Person('张三'18)
p.getPersonInfo()// 2. 继承 extends
// 定义一个Employee类继承Person
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() + `,我的工作部门是${this.department}`}
}
let emp = new Employee('王五', 26, '行政部')
emp.getEmployeeInfo()
emp.getPersonInfo()

2.6 掌握可迭代对象 ==> 当一个对象实现了 Symbol.iterator 属性时,它是可迭代的。一些内置的类型如 Array, Map, Set, String, Int32Array, Uint32Array 等都具有可迭代性。(可以通过for…of循环遍历的对象)

迭代器都是可迭代对象
可迭代对象是迭代器的一个子集

// 1. for..of 语句,针对可迭代对象才能使用,i 代表其中元素,不指下标。
let str: string = 'sdfs'
for(let i of str) {console.log(i) // s d f s
}// 2. map类型,每个元素都是由俩个组成:key,value
let map1 = new Map<string, number>()
map1.set('a', 111)
map1.set('b', 222)
map1.set('c', 333)
for(let j of map1) {console.log(j) // ['a', 111] ['b', 222] ['c', 333]console.log(j[1]) // 111 222 333
}

2.7 模块 ==> 前端模块化

3. ArkTs/ArkUI

  1. 继承 TS 的所有特性,是 TS 的超集。
  2. ArkUI 是一套构建分布式应用界面的声明式UI开发框架,只能使用表达式。组件中不需要renturn。
  3. 装饰器:
    3.1 @Entry:标识这是个入口组件,且表示是一个独立page页面,通过router进行跳转。
    3.2 @Component:自定义组件,可进行页面渲染。
    3.3 struct TextExample{}:定义名为TextExample的结构体,代表这个组件
    3.4 build() {}:定义build方法构建UI,类似ReactNative中的render方法,生成节点树实例。
  4. 自定义组件:
  5. 容器组件均支持子组件配置,可以实现相对复杂的多级嵌套。
    Column, Row, Flex, Stack, Grid, List 等组件都是容器组件
  6. 如果组件支持子组件配置,则需在尾随闭包 “{…}” 中为组件添加子组件的UI描述
  7. UI描述:
  8. 系统组件:由ArkUI直接提供的组件。
  9. 自定义组件:由开发者定义的组件。
  10. 属性方法:
  11. 事件方法:

3.1 配置组件的事件方法

事件方法以 “.” 链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行。

// 1. 使用箭头函数配置组件的事件方法
Button('Click me').onClick(() => {})// 2. 使用匿名函数表达式配置组件的事件方法,要求使用bind,以确保函数题中的this指向当前组件
Button('add counter').onClick(function(){}.bind(this))// 3. 使用组件的成员函数配置组件的事件方法,⚠️组件的成员函数只能在事件方法上面定义
myClickHandler(): void {}
Button('add counter').onClick(this.myClickHandler.bind(this))

3.2 类型定义

3.2.1 Resource 资源引用类型

// @Builder 自定义构建函数时,用 :Resource 表示资源引用类型
@Builder function CreateIcon (icon: Resource): void {Column() {Image(icon).width(28).height(28).objectFit(ImageFit.Contain).margin(10)}
}

3.2.2 Length 长度类型,用于描述尺寸单位

@Prop widthValue: Length = 0

3.2.3 ResourceStr 字符串类型,用于描述字符串入参可以使用的类型
3.2.4 Padding 内边距类型,用于描述组件不同方向的内边距
3.2.5 Margin

4. 容器组件

4.1 Column

  1. 是沿垂直方向布局容器
  2. 支持很多通用属性,如alignItems,justifyContent,width,height,border等
// 常用属性
Column({space: 5}){} // 创建一个column容器组件,设置子元素间的垂直间距为5,纵向布局元素垂直方向间距为5.
.alignItems(HorizontalAlign.Start)
.justifyContent(FlexAlign.Center)

4.2 Row

  1. 是沿水平方向布局容器
  2. 支持很多通用属性,如alignItems,justifyContent,width,height,border等
// 常用属性
Row({space: 5}){} // 横向布局元素间距
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Start)

4.3 List

  1. onReachEnd 事件:列表到底末尾位置时触发。
    List边缘效果为弹簧效果时,划动经过末尾位置时触发一次,回弹回末尾位置时再触发一次。

5. 基础组件

5.1 Text

  1. Text 组件是可以显示一段文本的组件。
// 常用属性:
.textAlign(TextAlign.Center)
.fontSize(14)
.fontColor()
.textOverflow({overflow: TextOverflow.Ellipsis}) // 设置文本溢出方式为省略号
.maxLines(1) // 设置最大行数为1
.border({width: 1})
.lineHeight(15) // 设置行高为20
.padding(10)

5.2 Image

// 引入项目本地 resources/base/media 路径下的图片
Image($r('app.media.ic_default'))// @Builder 自定义构建函数时,用 :Resource 表示资源引用类型
@Builder function CreateIcon (icon: Resource): void {Column() {Image(icon).width(28).height(28).objectFit(ImageFit.Contain).margin(10)}
}

5.3 ScrollBar

  1. 滚动条组件ScrollBar,用于配合可滚动组件使用,如List、Grid、Scroll

6. @Component 自定义组件

  1. @Component 装饰的UI单元,可以组合多个系统组件实现UI的复用。
  2. 由开发者定义的称为自定义组件
  3. 具有以下特点
    3.1 可组合:允许开发者组合使用系统组件,及其属性和方法。
    3.2 可重用:可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用。
    3.3 数据驱动UI更新,通过状态变量的改变,来驱动UI的刷新。

6.1 自定义组件通用样式

@Component
struct 自定义组件名 {build() {}
}

6.2 实现并引用自定义组件

// 自定义子组件
@Component
struct MyHelloComponent {@State username: string = '🧚‍'build(){Column() {Text(`hello ${this.username}`).fontSize(16).fontColor(Color.Grey).height(15).onClick(() => {this.username = '七喜'})}}
}
// 父组件
@Entry
@Component
struct UITest3 {build() {Column({space: 10}) {Text('主界面').fontSize(24).fontWeight(FontWeight.Bold).fontColor(Color.Black)MyHelloComponent({username: 'world'}) // 引用自定义组件Divider()MyHelloComponent({username: 'SZ'}) // 引用自定义组件}}
}

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

  1. 自定义组件:@Component 装饰的UI单元,可以组合多个系统组件实现UI的复用。
    组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:
    1.1 aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
    1.1 aboutToDisappear:在自定义组件即将析构销毁时执行。
  2. 页面:即应用的UI页面。可以由一个或多个自定义组件组成,@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry。
    只有被@Entry装饰的组件才可以调用页面的生命周期。页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:
    2.1 onPageShow:页面每次显示时触发。
    2.2 onPageHide:页面每次隐藏时触发一次。
    2.3 onBackPress:当用户点击返回按钮时触发。
  3. 流程图如下
    在这里插入图片描述

7.1 入口组件(页面)嵌套自定义组件的生命周期流程

  1. 页面组件aboutToAppear —> 自定义组件aboutToAppear —> 页面组件onPageShow(在页面组件点击删除自定义组件) --> 自定义组件aboutToDisappear
  2. 在页面组件跳转到另一个页面组件,页面组件onPageHide
  3. 在页面组件返回手机主屏,页面组件aboutToDisappear

8. @Builder 自定义构建函数

8.1 自定义组件内自定义构建函数

// 1. 定义语法
@Builder MyBuilderFunction() {}// 2. 使用语法
this.MyBuilderFunction() {}

8.2 全局自定义构建函数

// 1. 定义语法
@Builder function MyGlobalBuilderFunction() {}// 2. 使用语法
MyGlobalBuilderFunction() {}

8.3 参数传递规则

  1. 按引用传递参数
  2. 按值传递参数
    2.1 当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递
// 按引用传递参数
class obj {username: string = ''
}
@Builder function MyBuilder($$: obj) { // 参数需要直接传入对象字面量才会按引用传递该参数,其余传递方式均为按值传递。Column() {Text(`hello ${$$.username}`).fontSize(18).fontColor(Color.Grey)}
}@Entry
@Component
struct UITest4 {@State person_name: string = '花花' // 当传递的参数为状态变量时,状态变量的改变才会引起@Builder方法内的UI刷新build() {Column() {MyBuilder({username: this.person_name})Button('点击修改').margin({top: 8}).onClick(() => {this.person_name = '七喜'})}}
}

9. 状态管理

  1. 状态变量:被状态装饰器(@State) 装饰的变量叫状态变量,状态变量值的改变会引起UI的渲染更新。@State count: number = 0
  2. 常规变量:没有被状态装饰器修饰的变量,通常应用于辅助计算。它的改变永远不会引起UI的渲染更新。private/public increaseBy: number = 1
  3. 数据源/同步源:状态变量的原始来源,可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。
  4. 命名参数机制:父组件通过指定参数传递给子组件的状态变量,为父子传递同步参数的主要手段
  5. 从父组件初始化:父组件使用命名参数机制,将指定参数传递给子组件。子组件初始化的默认值在有父组件传值的情况下,会被覆盖。
  6. 初始化子组件:父组件中状态变量可以传递给子组件,初始化子组件对应的状态变量
  7. 本地初始化:在变量声明的时候赋值,作为变量的默认值。

9.1 @State装饰器 - 组件内状态 - 必须本地初始化

  1. 与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化
  2. 与子组件中的@Prop,@Link、@ObjectLink装饰变量之间建立双向数据同步。
// 自定义类型
class Person {public name: stringpublic age: numberconstructor(xName: string, xAge: number) {this.name = xNamethis.age = xAge}
}
// 自定义子组件
@Component
struct MyChild {@State person: Person = new Person('魔鬼', 18) // 指定自定义类型,初始化值private increase: number = 1build() {Column({space: 3}) {Text(`${this.person.name}、年龄${this.person.age}`).fontSize(18).fontWeight(500).height(60)Divider()Button(`修改姓名`).onClick(() => {this.person.name = this.person.name ===  '魔鬼' ? '天使': '魔鬼'}).width(200).height(60)Button('修改年龄').onClick(() => {this.person.age += this.increase}).width(200).height(60)}// .backgroundColor('#CCBBFF')}
}
// 父组件
@Entry
@Component
struct UITest5 {build() {Column({space: 30}) {MyChild({person: new Person('天使', 2), increase: 1})MyChild({increase: 2})MyChild()}}
}

9.2 @Prop装饰器 - 父子单向同步 - 允许本地初始化

  1. @Prop装饰变量时会进行深拷贝,在拷贝的过程中除了基本类型、Map、Set、Date、Array外,都会丢失类型。
  2. @Prop装饰器不能在@Entry装饰的自定义组件中使用。
  3. 被装饰变量的初始值,允许本地初始化。
// @Prop装饰器 - 父子单向同步
// 子组件
@Component
struct MyChild {@Prop age: number = 9 // 被装饰变量的初始值,允许本地初始化。当没有初始值时,页面显示undefinedprivate increase: number = 1build() {Column(){if (this.age >= 18) {Text(`子组件年龄${this.age}`).height(60)} else {Text(`未成年${this.age}`).height(60)}Button('减少子组件年龄').onClick(() => {this.age -= this.increase})}}
}
// 父组件
@Entry
@Component
struct UITest6{@State init_age: number = 14build() {Column() {Text(`父组件年龄${this.init_age}`).height(60)Button('增加父组件年龄').onClick(() => {this.init_age += 1})Divider()MyChild({age: this.init_age, increase: 2})Divider()MyChild()}}
}

9.3 @Link装饰器 - 父子双向同步 - 禁止本地初始化

  1. 父组件中@State, @StorageLink和@Link 和 子组件@Link可以建立双向数据同步。
  2. 被装饰变量的初始值,禁止本地初始化。

下面代码展示了class类对象类型、简单类型,俩种父子双向数据同步示例。

// @Link装饰器 - 父子双向同步。class类对象类型和简单类型俩种父子双向同步
// 自定义class类对象类型
class ButtonState {value: stringwidth: numberconstructor(value: string, width: number) {this.value = valuethis.width = width}
}
// 一号子组件 - class类对象类型
@Component
struct MyChildButton01 {// class类对象类型@Link buttonState: ButtonStatebuild() {Column() {Button(`${this.buttonState.value}${this.buttonState.width}`).width(this.buttonState.width).height(50).backgroundColor('#B088FF').fontColor('#FFFFFF,90%').onClick(() => {if (this.buttonState.width < 400) {this.buttonState.width += 50} else {this.buttonState = new ButtonState('一号本地', 100)}})}}
}
// 二号子组件 - 简单类型
@Component
struct MyChildButton02 {// 简单类型@Link buttonValue: string@Link buttonWidth: numberbuild() {Column() {Button(`${this.buttonValue}${this.buttonWidth}`).width(this.buttonWidth).height(50).backgroundColor('#FF7744').fontColor('#FFFFFF,90%').onClick(() => {this.buttonValue = this.buttonWidth < 400 ? this.buttonValue : '二号本地'this.buttonWidth = this.buttonWidth < 400 ? this.buttonWidth + 50 : 100})}}
}
// 父组件
@Entry
@Component
struct UITest6 {// class类对象类型@State parentButton: ButtonState = new ButtonState('一号子级', 100)// 简单类型@State parentValue: string = '二号子级'@State parentWidth: number = 100build() {Column({space: 10}) {Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Start }) {// class类对象类型从父组件@State向子组件@Link数据同步Button(`父组件中修改${this.parentButton.value}${this.parentButton.width}`).onClick(() => {this.parentButton.width = this.parentButton.width < 400 ? this.parentButton.width + 50 : 100})// 简单类型从父组件@State向子组件@Link数据同步Button(`父组件中修改${this.parentValue}${this.parentWidth}`).onClick(() => {this.parentWidth = this.parentWidth < 400 ? this.parentWidth + 50 : 100})// class类对象类型初始化@LinkMyChildButton01({ buttonState: $parentButton })// 简单类型初始化@LinkMyChildButton02({ buttonValue: $parentValue, buttonWidth: $parentWidth })}}}
}

10. if/else 条件渲染

  1. 渲染控制的能力
  2. 修改条件分支的时候,会把原来的条件分支组件先删除,再重新创建一个条件分支组件。
// if/else 条件渲染
// 子组件
@Component
struct MyChild {@Link count: number // @Link装饰器 - 父子双向同步 - 禁止本地初始化@Prop label: string // @Prop装饰器 - 父子单向同步 - 允许本地初始化build() {Row() {Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}) {Text(`${this.label}`)Button(`计数器${this.count}`).onClick(() => {this.count += 1})}// .backgroundColor('#B94FFF')}}
}
// 父组件
@Entry
@Component
struct UITest6 {@State flag: boolean = true // @State装饰器 - 组件内状态 - 必须本地初始化@State parentCount: number = 0build() {Column() {if (this.flag) {MyChild({ count: $parentCount, label: '真真' })} else {MyChild({ count: $parentCount, label: '假假' })}Button(`是否真假:${this.flag}`).onClick(() => {this.flag = !this.flag}).margin(10)}}
}

11. ForEach 循环渲染

  1. ForEach接口基于数组类型数据来进行循环渲染,需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。
    例如,ListItem组件要求ForEach的父容器组件必须为List组件。
  2. 数组类型数据
  3. 接受三个参数
  4. 和 if…else 不一样,会做二次渲染,可以做到单独修改某一个值。
// ForEach 循环渲染
// 子组件
@Component
struct MyChild {@Prop label: stringbuild() {Column() {Text(this.label).fontSize(24).fontColor("#9955FF")}}
}
// 父组件
@Entry
@Component
struct UITest7 {@State my_array: Array<string> = ['one', 'two', 'three']build() {Column({space: 15}) {ForEach(this.my_array, (item: string, index: number) => { // ForEach接口基于数组类型数据来进行循环渲染,需要与容器组件配合使用。MyChild({label: item}) // 且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。})Button('修改two').onClick(() => {this.my_array[1] = 'new_two' // 和 if..else 不一样,会做二次渲染,可以做到单独修改某一个值。})}.justifyContent(FlexAlign.Center).height("100%").width('100%')}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com