收集官网faq:https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-kit-0000001769732210
一. ArkTS&ArkUI
1. 基础理论
1. 鸿蒙相关的生命周期都有哪些?
(1)UIAbility生命周期: onCreate、onWindowStageCreate、onForeground、onBackground、onWindowStageDestroy、onDestroy。
onCreate:Create状态为在应用加载过程中,UIAbility实例创建完成时触发,系统会调用onCreate()回调。可以在该回调中进行页面初始化操作,例如变量定义资源加载等,用于后续的UI展示。
onWindowStageCreate():UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调,可以在该回调中设置UI加载、设置WindowStage的事件订阅。[事件订阅代码]
onForegound():在UIAbility的UI可见之前,如UIAbility切换至前台时触发。可以在onForeground()回调中申请系统需要的资源,或者重新申请在onBackground()中释放的资源。
onWindowStageDestory():在UIAbility实例销毁之前,则会先进入onWindowStageDestroy()回调,可以在该回调中释放UI资源。
onBackground():在UIAbility的UI完全不可见之后,如UIAbility切换至后台时候触发。可以在onBackground()回调中释放UI不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等。
onWindowStageDestory():在UIAbility实例销毁之前,则会先进入onWindowStageDestroy()回调,可以在该回调中释放UI资源。
onDestroy():Destroy状态在UIAbility实例销毁时触发。可以在onDestroy()回调中进行系统资源的释放、数据的保存等操作。
(2)页面生命周期:onPageShow、onPageHide、onBackPress。 页面生命周期,说白了就是@Entry修饰的组件,才称之为页面。
- onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效。
- onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效。
- onBackPress:当用户点击返回按钮时触发,仅@Entry装饰的自定义组件生效。
(3)组件生命周期:aboutToAppear(发起网络请求)、aboutToDisappear。
- aboutToAppear:在创建自定义组件的新实例后,在执行其build()函数之前执行。允许在aboutToAppear函数中改变状态变量,更改将在后续执行build()函数中生效。
- aboutToDisappear:函数在自定义组件销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。
按返回键页面执行生命周期方法:
- 打开第一个页面:
- Index:aboutToAppear
- Index:onPageShow
- 跳转第二个页面:
- Index:onPageHide
- Second:aboutToAppear
- Second:onPageShow
- 点击back:如果是在第二个页面跳转到第一个页面:
- Second:onBackPress Second:onPageHide
- Second:onPageHide Index:aboutToAppear
- Index:onPageShow Index:onPageShow
- Second:aboutToDisappear
返回页面不走aboutToAppear:
- aboutToAppear函数在创建自定义组件的新实例后,在执行其build()函数之前执行。
- 返回页面时==不需要走重新创建==,不会执行aboutToAppear,只会执行onPageShow。
问:aboutToAppear和onAppear的区别?
aboutToAppear:是组件的生命周期方法,当组件实例创建后,执行build函数之前执行aboutToAppear
-
onAppear:是组件的属性方法,在该组件显示时触发此回调
Text() .onAppear(()=>{}
2. ArkUI的两大开发范式是什么,区别是什么
- ArkUI推荐使用声明式开发范式 , 其他的框架有参考类Web开发范式
类Web开发范式:采用经典的HML、CSS、JavaScript三段式开发方式,即使用HML标签文件搭建布局、使用CSS文件描述样式、使用JavaScript文件处理逻辑。该范式更符合于Web前端开发者的使用习惯,便于快速将已有的Web应用改造成方舟UI框架应用。
声明式开发范式:采用基于TypeScript声明式UI语法扩展而来的ArkTS语言,从组件、动画和状态管理三个维度提供UI绘制能力。
-
==延伸问题==:有听过命令式编程么,命令式编程与声明式编程的区别是什么?
- 其实这里的命令式编程,就相当于是类Web开发范式
我们来对比一下命令式和声明式
声明式开发范式 : 只需要描述/声明 , 你要做什么 (通过封装好的组件以及相关熟悉方法 , 快速实现目的)
命令式开发范式 : 不仅需要知道做什么 , 更需要知道如何做 (需要通过最原始的方式 , 一步一步实现)
-
左侧是纯前端实现的按钮点击 , 也就是命令式
- 命令式需要自己一点一点实现 , 如何做必须清楚
-
右侧是ArkTs实现的按钮点击 , 也就是声明式
- 声明式只需要知道做什么 , 调用对应api即可 , 不需要知道内部如何实现
3. 项目使用的是harmoneyos还是openharmoney,区别是啥 ?
HarmonyOS:OpenHarmony+闭源应用和华为移动服务HMS(比如应用市场,视频,音乐等app)
Andoird:aosp(android open source project) + GMS(Google Mobile Service)
4. arkts中哪些类不能被继承, 面试官关注点是组件是否可以继承?(组件是否可以被继承)
组件不能被继承,被@compent修饰的自定义组件不能被继承,只能引用,或者对外暴露方法。
官网解释:
struct:自定义组件基于struct实现,struct + 自定义组件名 + {...}的组合构成自定义组件,==不能有继承关系==。对于struct的实例化,可以省略new。
-
@Component:@Component装饰器仅能装饰struct关键字声明的数据结构。==struct被@Component装饰后具备组件化的能力==,需要实现build方法描述UI,一个struct只能被一个@Component装饰。@Component可以接受一个可选的bool类型参数。
@Component struct MyComponent { }
5.介绍Stage模型和FA模型
- Stage模型 : HarmonyOS 3.1推出 也就是API9 , 是目前==主推==且会长期演进的模型
- 由于提供了AbilityStage、WindowStage等类作为应用组件和Window窗口的“舞台”,因此称这种应用模型为Stage模型
- stage: 舞台 /steɪdʒ/
- FA模型: FA(Feature Ability)模型:HarmonyOS早期版本开始支持的模型,已经不再主推
- feature: 特点 /ˈfiːtʃə(r)/
- 区别: Stage模型与FA模型最大的区别在于
- Stage模型中,多个应用组件共享同一个ArkTS引擎实例;
- 而FA模型中,每个应用组件独享一个ArkTS引擎实例。
- 因此在Stage模型中,应用组件之间可以方便的共享对象和状态,同时减少复杂应用运行对内存的占用。
- Stage模型作为主推的应用模型,开发者通过它能够更加便利地开发出分布式场景下的复杂应用。
2. 装饰器
1. 你使用过哪些装饰器,分别阐述一下他们得作用
@State装饰器,使得变量变为状态变量,影响UI(数据变化,UI变化)
-
@Prop装饰的变量和父组件建立单向的同步关系:
父组件的@State数据变化,会同步到子组件@Prop
-
具体用法
//父组件:Parent @State num:number = 0 build(){ Son({num:this.num}) } //子组件:Son @Prop num:number
@Prop修饰的变量,api9不能初始化,api11能初始化
-
@Link装饰的变量与其父组件中的数据源共享相同的值。
父组件的@State数据变化,会同步到子组件@Link数据
子组件@link数据变化,会同步到父组件@State数据
-
具体用法
//父组件:Parent @State num:number = 0 build(){ Son({num:$num})//api9必须使用$,api11开始也可以使用this了 } //子组件:Son @Link num:number
@Link修饰的变量,api9不能初始化,api11不能初始化
2. 有用过@Styles,@Extend,@Builder装饰器么?
-
@Styles装饰器:定义组件重用样式 (多个组件通用的样式)
@Styles装饰器,用于封装重复的通用样式代码。
如果多个不同类型的组件,有着相同的样式,例如宽高,背景色,字体大小。那么就可以将这下相同的样式代码抽取到一个@Styles装饰器修饰的方法中,供大家复用。
支持全局和局部定义:
// 全局 @Styles function functionName() { ... } //styles方法不能调用另一个styles方法*** // 在组件内 @Component struct FancyUse { @Styles fancy() { .height(100) } }
-
@Extend装饰器:定义扩展组件样式 (某一种组件自己的样式,私有属性)
@Extend,用于扩展原生组件样式。
如果同一类型的组件,有着很多相同的样式,例如按钮的类型,点击事件等。那么就可以将这些重复代码,抽过去到一个@Extend装饰器修饰的方法中,供此组件使用。
==仅支持全局定义==:(因为它相当于是给所有的此类组件使用)
// @Extend(Text)可以支持Text的私有属性fontColor @Extend(Text) function fancy () { .fontColor(Color.Red) } // superFancyText可以调用预定义的fancy @Extend(Text) function superFancyText(size:number) { //Extend方法可以调用另一个Extend方法 .fontSize(size) .fancy() }
-
@Builder装饰器:自定义构建函数
@Builder装饰器,用于封装重复的,复杂UI结构代码,例如List中的ListItem的布局结构,一般比较复杂就可以抽取到@Builder装饰的函数中
@Builder所装饰的函数遵循build()函数语法规则,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。
支持全局定义和局部定义:
//既然调用是通过this调用,那么说明是在组件内部定义 //组件内部定义不需要关键字function @Builder MyBuilderFunction() { ... } //全局定义 MyGlobalBuilderFunction()
-
@BuilderParam装饰器:引用@Builder函数
当开发者创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操作。若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能。
为解决此问题,ArkUI引入了@BuilderParam装饰器,该装饰器用于声明任意UI描述的一个元素,==类似slot占位符==。
- 使得自定义组件更加灵活
代码:
@Component struct Child { @Builder customBuilder() {} // 使用父组件@Builder装饰的方法初始化子组件@BuilderParam @BuilderParam customBuilderParam: () => void = this.customBuilder; build() { Column() { this.customBuilderParam() } } } @Entry @Component struct Parent { @Builder componentBuilder() { Text(`Parent builder `) } build() { Column() { Child({ customBuilderParam: this.componentBuilder }) } } }
3. 还用过其他装饰器么?
1. Provide和Consume
-
@Provide和@Consume,用于祖先与后代组件的双向数据同步,实现跨层级传递
- ==理解==:@Provide装饰器的变量是在祖先组件中,可以理解为被“提供”给后代的状态变量。@Consume装饰的变量是在后代组件中,去“消费”数据
-
语法特点:
-
@Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定
// 通过相同的变量名绑定 @Provide a: number = 0; //祖先组件中定义 @Consume a: number; //子孙组件中定义 // 通过相同的变量别名绑定 @Provide('a') b: number = 0;//参数即为别名 @Consume('a') c: number;
-
2. ObjectLink和Observed
-
@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:
- 被@Observed装饰的类,可以被观察到属性的变化;
- 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。
- 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。
详情参考: 3.状态管理.md --> 2.5章节
3. Watch
概述
- @Watch应用于==对状态变量的监听==。如果开发者需要关注某个状态变量的值是否改变,可以使用@Watch为状态变量设置回调函数。
- @Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用
代码:
@Component
struct TotalView {
@Prop @Watch('onCountUpdated') count: number = 0;
@State total: number = 0;
// 该函数是自定义组件的成员函数
// @Watch 回调
// propName是被watch的属性名
// 多个状态绑定同一个@Watch回调时,通过propName区分到底是哪个状态改变了
onCountUpdated(propName: string): void {
this.total += this.count;
}
build() {
Text(`Total: ${this.total}`)
}
}
@Entry
@Component
struct CountModifier {
@State count: number = 0;
build() {
Column() {
Button('add to basket')
.onClick(() => {
this.count++
})
TotalView({ count: this.count })
}
}
}