关于ArkTS和ArkUI,基础语法请看👉官方开发手册
系统学习后,聊聊几个点,面向刚学习这门语言的小白,用于巩固和回顾😋
目录
类型推断应用
函数相关
布局方式
线性布局
堆叠布局
网格布局
弹性布局
渲染相关
条件渲染
循环渲染
ForEach
Repeat
数据懒加载
装饰器
数据传递装饰器
省流版
@State
@Prop
@Link
@Provide 和 @Consume
@Observed
@ObjectLink
增强和扩展组件功能
@Extender
@Style
@Builder
自定义组件
创建自定义组件
使用 @Prop 动态传参
使用 @State 和 @Link 管理状态
使用 @BuilderParam 传递UI
router-路由操作
页面跳转和后退
页面栈
路由模式
路由传参
Stage模型
App
Module
Ability
关系和交互
生命周期流程示例
类型推断应用
类型推断是一种自动确定表达式类型的机制,能够在不明确指定类型的情况下,通过代码的上下文来推断出合适的类型。这种机制有助于减少代码中的类型声明,使代码更简洁和易于维护。
变量声明与初始化: 当你在声明变量时赋予它初始值,ArkTS会根据初始值推断变量的类型。
let num = 10; // 推断为number类型
let str = "Hello"; // 推断为string类型
let isTrue = true; // 推断为boolean类型
let numbers = [1, 2, 3]; // 推断为number[]
let user = {name: "Alice",age: 30
}; // 推断为{ name: string, age: number }
函数返回值: ArkTS会根据函数体内的返回值来推断函数的返回类型。
function getNumber() {return 42; // 推断返回值类型为number
}
类型兼容性: ArkTS会在需要的时候推断出类型,以确保类型之间的兼容性。
let x = "hello";
let y = x; // 推断y的类型为string
泛型推断: 在使用泛型时,ArkTS会根据传入的参数类型来推断泛型的具体类型。
function identity<T>(arg: T): T {return arg;
}let output = identity(42); // 推断T为number,output的类型为number
函数相关
常规函数声明
这种方式使用 function
关键字,可以显式声明参数和返回值类型。
function add(x: number, y: number): number {return x + y;
}
函数表达式
函数表达式可以赋值给变量,变量的类型可以显式声明。
const multiply: (x: number, y: number) => number = function(x, y) {return x * y;
};
箭头函数
箭头函数是一种简洁的函数声明方式,常用于简化函数表达式。
const subtract = (x: number, y: number): number => {return x - y;
};
重载函数
通过定义多个函数签名,可以实现函数重载。
function double(x: number): number;
function double(x: string): string;function double(x: any): any {if (typeof x === "number") {return x * 2;} else if (typeof x === "string") {return x + x;}
}
泛型函数
泛型函数允许你定义可复用的函数,并在调用时指定类型。
function identity<T>(arg: T): T {return arg;
}let output1 = identity<string>("myString"); // T 被推断为 string
let output2 = identity<number>(100); // T 被推断为 number
参数
在ArkTS中,函数参数的处理可以非常灵活,包括可选参数、默认参数和剩余参数。这三者的综合运用可以使函数声明更加简洁和强大,适应多种调用场景。
①可选参数和默认参数
可选参数和默认参数在函数声明中都允许函数参数有灵活性:
- 可选参数使用
?
标识,使参数可以在调用时省略。 - 默认参数为参数提供默认值,当调用时未提供该参数时使用默认值。
function greet(name: string, greeting: string = "Hello", punctuation?: string): string {if (punctuation) {return `${greeting}, ${name}${punctuation}`;} else {return `${greeting}, ${name}!`;}
}// 示例调用
console.log(greet("Alice")); // 输出: Hello, Alice!
console.log(greet("Bob", "Hi")); // 输出: Hi, Bob!
console.log(greet("Charlie", "Hey", "?")); // 输出: Hey, Charlie?
②剩余参数
剩余参数使用 ...
语法,可以将多个参数收集到一个数组中,用于处理变长参数列表的场景。
function sum(...numbers: number[]): number {return numbers.reduce((acc, curr) => acc + curr, 0);
}// 示例调用
console.log(sum(1, 2, 3)); // 输出: 6
console.log(sum(10, 20, 30, 40)); // 输出: 100
布局方式
线性布局
线性布局是最常用的布局之一,它将子元素沿水平方向或垂直方向排列。
特性:
- 可以设置子元素的排列方向:水平(row)或垂直(column)。
- 支持设置子元素之间的间距。
示例:
import { Column, Text, Button } from 'ArkUI';Column() {Text('Item 1');Text('Item 2');Button('Click Me');
}
堆叠布局
堆叠布局将子元素堆叠在一起,所有元素都位于同一个坐标位置。
特性:
- 子元素可以相互覆盖。
- 常用于需要将多个元素叠加在一起的场景,如图像叠加文本。
示例:
import { Stack, Image, Text } from 'ArkUI';Stack() {Image('background.png');Text('Overlay Text');
}
网格布局
网格布局将子元素按照网格进行排列,子元素可以跨行或跨列。
特性:
- 灵活的行列定义。
- 支持子元素跨行、跨列。
示例:
import { Grid, Text } from 'ArkUI';Grid(columns="1fr 1fr", rows="auto auto") {Text('Item 1').gridColumn("1 / 2").gridRow("1");Text('Item 2').gridColumn("2 / 3").gridRow("1");Text('Item 3').gridColumn("1 / 3").gridRow("2");
}
弹性布局
弹性布局提供了一种灵活的布局方式,适用于复杂的自适应布局。
特性:
- 支持主轴和交叉轴上的元素对齐和分布。
- 子元素可以按比例缩放。
示例:
import { Flex, Text } from 'ArkUI';Flex(direction="row", justifyContent="space-between") {Text('Item 1');Text('Item 2');Text('Item 3');
}
渲染相关
条件渲染
条件渲染允许你根据某些条件来决定是否渲染某个UI组件。通常使用三元运算符或 if
语句来实现。
示例:
@Entry
@Component
struct ConditionalRenderingComponent {@State isVisible: boolean = true;build() {Column() {if (this.isVisible) {Text('This text is visible');} else {Text('This text is hidden');}Button(this.isVisible ? 'Hide' : 'Show').onClick(() => {this.isVisible = !this.isVisible;});}}
}
循环渲染
循环渲染用于根据数组数据来渲染一组相似的UI组件。在ArkUI中,常用的循环渲染方法有 ForEach
和 Repeat
。
ForEach
ForEach
用于遍历数组并渲染每个元素。
示例:
@Entry
@Component
struct ForEachComponent {@State items: string[] = ['Item 1', 'Item 2', 'Item 3'];build() {Column() {ForEach(this.items, (item) => {Text(item);});}}
}
Repeat
Repeat
用于渲染一定次数的相同组件。
示例:
@Entry
@Component
struct RepeatComponent {build() {Column() {Repeat(5, (index) => {Text(`Item ${index + 1}`);});}}
}
数据懒加载
数据懒加载用于按需加载数据,从而提高应用性能和响应速度。通常在需要加载大量数据时使用懒加载。
@Entry
@Component
struct LazyLoadComponent {@State data: string[] = [];@State hasMoreData: boolean = true;onInit() {this.loadData();}loadData() {// 模拟数据加载setTimeout(() => {this.data = this.data.concat(['New Item 1', 'New Item 2', 'New Item 3']);this.hasMoreData = this.data.length < 20; // 假设最多加载20条数据}, 1000);}build() {Column() {ForEach(this.data, (item) => {Text(item);});if (this.hasMoreData) {Button('Load More').onClick(() => {this.loadData();});}}}
}
装饰器
什么是装饰器?
类似java的注解
用于装饰类、结构、方法以及变量,并赋予其特殊的含义。如@Entry、@Component和@State都是装饰器,@Component表示自定义组件,@Entry表示该自定义组件为入口组件,@State表示组件中的状态变量,状态变量变化会触发UI刷新。
数据传递装饰器
从数据的传递形式和同步类型层面看,装饰器也可分为:
-
只读的单向传递;
-
可变更的双向传递。
省流版
- @State:管理组件内部状态,不涉及组件间数据传递。
- @Prop:接收父组件传递的只读数据,用于单向数据流。
- @Link:实现双向数据绑定,允许子组件修改父组件的数据。
- @Provide 和 @Consume:用于跨组件层级的数据共享,适用于全局状态管理。
- @Observed:监视对象属性变化,自动触发组件重新渲染。
- @ObjectLink:传递复杂对象,允许子组件修改,适用于复杂数据管理。
@State
功能:
- 声明组件内部的状态。
- 状态数据变化时,组件自动重新渲染。
使用场景:
- 用于组件内部的可变数据管理,不涉及组件之间的数据传递。
示例:
@State count: number = 0;
区别:
- 仅限于组件内部使用,不会与其他组件共享。
@Prop
功能:
- 接收父组件传递的属性(props)。
- 这些属性是只读的,不能在子组件中修改。
使用场景:
- 用于子组件从父组件接收数据。
示例:
@Prop title: string;
区别:
- 只读属性,适用于父组件向子组件传递数据,不支持数据的双向绑定。
@Link
功能:
- 创建组件之间的数据连接,允许子组件修改父组件的数据。
- 支持数据的双向绑定。
使用场景:
- 用于需要子组件与父组件共享和修改数据的场景。
示例:
@Link count: number;
区别:
- 与
@Prop
相比,@Link
允许双向数据绑定,子组件可以更新父组件的数据。
@Provide 和 @Consume
功能:
@Provide
:在祖先组件中提供数据。@Consume
:在后代组件中消费数据。
使用场景:
- 用于跨多个组件层级的数据共享。
示例: 祖先组件:
@Provide theme: string = "dark";
后代组件:
@Consume theme: string;
区别:
- 适用于需要跨越多层组件共享数据的场景,与
@State
和@Prop
不同,主要用于更广泛的数据分发。
@Observed
功能:
- 监视对象属性的变化。
- 属性变化时,组件自动重新渲染。
使用场景:
- 用于需要自动追踪和响应数据变化的场景。
示例:
@Observed userName: string = "John";
区别:
- 与
@State
类似,但更适用于需要自动监视对象属性变化的场景。
@ObjectLink
功能:
- 允许复杂对象作为属性传递给子组件。
- 子组件可以修改这些对象。
使用场景:
- 用于复杂数据结构的共享和管理。
示例:
@ObjectLink userDetails: User;
区别:
- 与
@Link
相似,但专门用于复杂对象的数据共享和管理。
增强和扩展组件功能
在ArkTS中,@Extender
、@Style
和 @Builder
是特定的注解,用于增强和扩展组件的功能。
@Extender
功能:
@Extender
注解用于扩展现有的组件或类,允许在现有功能的基础上添加新的行为或属性。
使用场景:
- 当需要在不修改原组件或类的情况下,增加或修改功能时使用。
示例:
@Extender
class ExtendedComponent extends BaseComponent {newMethod() {// 新的方法或属性}
}
区别:
@Extender
注解的主要作用是增强现有类或组件的功能,类似于装饰器模式。
@Style
功能:
@Style
注解用于为组件应用特定的样式,允许开发者在组件声明中直接定义样式。
使用场景:
- 在需要为组件指定特定样式或动态样式时使用。
示例:
@Style({backgroundColor: 'blue',fontSize: '14px'
})
class StyledComponent {// 组件逻辑
}
区别:
@Style
注解使得样式定义与组件逻辑紧密结合,增强了样式管理的灵活性。
@Builder
功能:
@Builder
注解用于构建复杂对象或组件,简化了对象的创建过程,通常用于需要链式调用来设置属性的场景。
使用场景:
- 当需要简化复杂对象或组件的创建过程,使用链式调用设置多个属性时使用。
示例:
@Builder
class ComplexObject {private prop1: string;private prop2: number;setProp1(value: string): this {this.prop1 = value;return this;}setProp2(value: number): this {this.prop2 = value;return this;}build(): ComplexObject {return this;}
}// 使用示例
const obj = new ComplexObject().setProp1("value1").setProp2(123).build();
区别:
@Builder
注解通过链式调用的方式简化了复杂对象的构建过程,增强了代码的可读性和可维护性。
自定义组件
创建自定义组件
自定义组件通过 @Component
装饰器定义。以下是一个简单的示例:
示例:创建一个简单的 MyButton
组件。
@Component
struct MyButton {@Prop label: string;build() {Button(this.label).onClick(() => {console.log(`${this.label} clicked`);});}
}
在这个示例中,MyButton
组件接收一个 label
属性,并在按钮点击时输出该标签。
使用 @Prop
动态传参
@Prop
注解用于父组件向子组件传递数据。
示例:
@Entry
@Component
struct HomePage {build() {Column() {MyButton({ label: 'Click Me' });}}
}
使用 @State
和 @Link
管理状态
示例
HomePage
组件使用@State
管理parentCount
。Counter
组件通过@Link
接收并修改count
,实现与父组件HomePage
的数据绑定和状态同步。
// HomePage.ets
@Entry
@Component
struct HomePage {@State parentCount: number = 0;build() {Column() {Text(`Parent Count: ${this.parentCount}`);Counter({ count: this.parentCount });}}
}// Counter.ets
@Component
struct Counter {@Link count: number;build() {Column() {Text(`Count: ${this.count}`);Button('Increment').onClick(() => {this.count += 1;});}}
}
使用 @BuilderParam
传递UI
@BuilderParam
注解用于在组件间传递UI组件,特别适用于需要传递复杂布局或多个UI组件的场景。
示例:创建一个可以接收自定义标题和内容的 CustomCard
组件。
CustomCard.ets:
@Component
struct CustomCard {@BuilderParam title: () => any;@BuilderParam content: () => any;build() {Column() {this.title();this.content();}}
}
使用 CustomCard
的父组件:
@Entry
@Component
struct HomePage {build() {CustomCard({title: () => Text('Custom Title'),content: () => Text('This is the custom content of the card.')});}
}
router-路由操作
页面跳转和后退
使用 router
对象可以在不同页面之间进行跳转和后退操作。
页面跳转
在页面跳转时,还可以使用 pushUrl
和 replaceUrl
方法。
pushUrl:用于在当前页面栈中推入新页面。
router.pushUrl('pages/DetailsPage');
replaceUrl:用于替换当前页面栈的顶层页面。
router.replaceUrl('pages/DetailsPage');
页面栈
页面栈管理用于维护页面的导航历史,允许应用程序在多个页面之间前进和后退。
页面栈内最多32个页面。
路由模式
ArkUI支持两种路由模式:standard
和 single
。
- standard模式:标准模式,每次跳转页面都会创建一个新的实例。
- single模式:单实例模式,每次跳转页面时,都会使用同一个页面实例,类似于单例模式。
设置路由模式:
router.setMode('standard'); // 或 'single'
路由传参
在页面跳转时,可以通过路由传递参数,这些参数可以在目标页面中获取。
传递参数:
router.push({url: 'pages/DetailsPage',params: {id: 123,name: 'example'}
});
接收参数:
@Entry
@Component
struct DetailsPage {@State id: number;@State name: string;onInit() {const params = router.getParams();this.id = params.id;this.name = params.name;}build() {Column() {Text(`ID: ${this.id}`)Text(`Name: ${this.name}`)}}
}
Stage模型
在ArkUI中,Stage模型是HarmonyOS的应用开发模型之一,它管理应用程序的生命周期和各个组件的交互。Stage模型由App、Module和Ability三部分组成。下面详细介绍它们之间的关系和生命周期。
App
功能:
- App是应用程序的根实体,代表整个应用的生命周期。它是应用的入口,管理应用的初始化、全局状态和资源。
生命周期:
onCreate()
: 应用启动时调用,用于进行初始化操作。onDestroy()
: 应用销毁时调用,用于进行清理操作。
示例:
@Entry
class MyApp extends App {onCreate() {console.log('Application created');}onDestroy() {console.log('Application destroyed');}
}
Module
功能:
- Module是应用程序的功能模块,一个应用可以包含多个模块,每个模块可以包含多个Ability。
- Module通过配置文件(config.json)定义,描述了模块的能力、权限等信息。
示例: config.json:
{"module": {"abilities": [{"name": "MainAbility","label": "Main Ability","icon": "$media:icon","type": "page"}],"deviceType": ["phone", "tablet"],"requiredPermissions": ["INTERNET"]}
}
Ability
功能:
- Ability是应用程序的核心组件,代表应用的具体功能。主要有两种类型:Page Ability和Service Ability。
- Page Ability:用于管理界面和用户交互。
- Service Ability:用于后台任务和服务。
生命周期:
-
Page Ability:
onStart()
: Ability启动时调用。onActive()
: Ability进入前台时调用。onInactive()
: Ability进入后台时调用。onBackground()
: Ability完全进入后台时调用。onStop()
: Ability停止时调用。
-
Service Ability:
onStart()
: Ability启动时调用。onCommand()
: 接收并处理命令。onStop()
: Ability停止时调用。
示例: MainAbility.ts:
@Entry
class MainAbility extends Ability {onStart(intent) {console.log('MainAbility started');}onActive() {console.log('MainAbility active');}onInactive() {console.log('MainAbility inactive');}onBackground() {console.log('MainAbility background');}onStop() {console.log('MainAbility stopped');}
}
关系和交互
- App 是整个应用的入口,管理全局状态和资源。
- Module 是应用的功能模块,包含一个或多个Ability。
- Ability 是具体功能的实现单元,可以是页面能力(Page Ability)或者服务能力(Service Ability)。
它们通过配置文件(如config.json)和生命周期方法进行交互和管理。
生命周期流程示例
一个典型的应用启动和运行流程如下:
- 应用启动:App的
onCreate()
方法被调用,应用初始化。 - Module加载:根据config.json加载模块和权限。
- Ability启动:Ability的
onStart()
方法被调用。 - Ability进入前台:Ability的
onActive()
方法被调用。 - Ability进入后台:Ability的
onInactive()
和onBackground()
方法被依次调用。 - Ability停止:Ability的
onStop()
方法被调用。 - 应用销毁:App的
onDestroy()
方法被调用,应用资源被清理。
通过这种层次化的设计,Stage模型使得应用的开发和管理更加模块化、清晰和高效。