1、ArkTs是什么?
ArkTS是HarmonyOS优选的主力应用开发语言。保持了TypeScript的基本风格,同时通过规范定义强化开发期静态检查和分析,提升程序执行稳定性和性能。
ArkTS的主要特点包括:
静态类型检查:ArkTS在编译时进行类型检查,这有助于在代码运行前发现和修复错误,提高代码的稳定性和性能。
声明式UI:ArkTS定义了声明式UI描述,允许开发者以更简洁、更自然的方式开发跨端应用。
状态管理:ArkTS提供了多维度的状态管理机制,使得与UI相关联的数据可以在组件内使用,也可以在不同组件层级间传递,支持单向和双向数据流。
渲染控制:ArkTS支持条件渲染、循环渲染和数据懒加载,允许开发者根据应用的不同状态渲染UI内容。
兼容性:ArkTS兼容TS/JavaScript生态,开发者可以使用TS/JS进行开发或复用已有代码。
并发机制: ArkTS支持轻量化的并发机制,允许开发者编写并发代码,提高应用的性能和响应速度。
2、ArkTS和TS有什么区别?
ArkTs 基于 Ts 做了扩展,并且强化了静态检查和分析
一、扩展了 UI:
定义了声明式 UI 描述、自定义组件,事件方法、属性方法
提供了多维度的状态管理机制
提供了控制渲染、循环渲染的能力
二、强化了检查
不支持 var、any、unknown、Symbol
不支持解构赋值
不支持使用对象字面量进行类型声明
不支持在运行时动态增删对象的属性
不支持在函数内声明函数
不支持使用 typeof 作为类型
不支持使用 # 符号开头声明的私有字段,改用 private 关键字
不支持把 function 定义函数赋值给变量,改为使用箭头函数
3、Object、object、ESObject有什么区别?
- Object类型是所有引用类型的基类型。任何值,包括基本类型的值(它们会被自动装箱),都可以直接被赋给Object类型的变量。
- object类型则用于表示除基本类型外的类型。
- ESObject类型则用于在ArkTS跨语言调用的场景中,用以标注JS/TS对象的类型
4、Record<K, V>和HashMap<K, V>有什么区别?
Record是一种轻量级的数据结构,主要用于存储一组固定数量的、类型明确的键值对。与HashMap相比,Record的主要特点是不可变性(immutable)和结构化。Record一旦创建,其包含的键值对就不能被修改,这有助于保证数据的完整性和安全性。Record适用于需要在多个组件或方法间传递固定结构数据的场景,例如,传递配置参数或返回结果集。
HashMap则是一种基于哈希表的键值对集合,允许动态添加、删除和更新键值对。通过哈希函数将键映射到值上。适合于快速的键值查找操作,时间复杂度为O(1)。HashMap的主要优点是灵活性高、访问速度快。HashMap适用于需要频繁修改键值对集合的场景,例如,实现缓存或实现自定义的数据结构。HashMap是ArkTS提供的高性能容器类,底层使用红黑树实现,提供了高性能的数据读写操作,适合用于实现快速读写键值。
Records是用来表示单个数据实体的结构。在HarmonyOS 鸿蒙Next系统中,选择使用Record还是HashMap主要取决于具体的应用场景和需求。如果需要存储和传递固定结构的数据,且不希望数据在传递过程中被修改,那么Record是更好的选择。如果需要动态管理键值对集合,那么HashMap则更为合适。
5、页面和自定义组件生命周期有哪些?
页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:
- [onPageShow]:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。
- [onPageHide]:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
- [onBackPress]:当用户点击返回按钮时触发。
组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:
- [aboutToAppear]:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
- [onDidBuild]:组件build()函数执行完成之后回调该接口,开发者可以在这个阶段进行埋点数据上报等不影响实际UI的功能。不建议在onDidBuild函数中更改状态变量、使用animateTo等功能,这可能会导[图片上传失败...(image-f5c490-1742384150320)]
致不稳定的UI表现。 - [aboutToDisappear]:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。
6、父子组件如何通信?
父子单向数据传递 @State @Prop
@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。
父子双向数据传递 @State @Link 、@objectLink @Link
子组件中被 @Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。
跨组件通信 @Provide装饰器和 @Consume装饰器
@Provide和 @Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于 @Prop和 @Link,@Provide和 @Consume摆脱参数传递机制的束缚,实现跨层级传递。
@Observed装饰器和 @ObjectLink装饰器
对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就要用到 @Observed/@ObjectLink装饰器
注意:@ObjectLink装饰器不能在 @Entry装饰的自定义组件中使用且 @ObjectLink 装饰的变量不能被赋值,只能对其属性进行赋值操作
7、兄弟组件如何通信?
通过公共父组件传递
如果两个组件是同一个父组件的子组件,可以通过父组件来传递数据或事件。父组件可以作为中介,将一个子组件的数据或事件传递给另一个子组件。
使用全局状态管理
使用全局状态管理(如 AppStorage、LocalStorage)来存储共享数据。兄弟组件可以独立地读取和更新这个全局状态,从而实现通信。
8、如何实现页面间的通信?
使用 @Provide和 @Consume装饰器
使用路由跳转传参
使用导航跳转传参
9、跨设备通信的方式有哪些?
HarmonyOS支持多种跨设备通信方式,包括:
- 分布式软总线:一种高性能的通信机制,允许设备之间建立直接连接,进行数据传输。
- 蓝牙:使用标准的蓝牙技术进行设备间的通信。
- WLAN:通过WLAN网络实现设备间的通信。
- 远程服务调用:通过分布式任务调度实现跨设备的服务调用。
10、如何进行全局状态管理?
@Provide @Consume装饰器
适用场景:适用于整个组件树而言“全局”的状态共享,且该状态改动不频繁的场景。
工作原理:通过在最顶层组件中使用 @Provide装饰器提供状态,其他需要共享状态的组件通过 @Consume装饰器获取该状态 。
优点:减少了状态传递的层级,提升了代码的可维护性和可拓展性。
注意事项:确保状态的生命周期与组件树的生命周期一致,避免不必要的UI刷新。
AppStorage
适用场景:适用于整个应用而言“全局”的变量或应用的主线程内多个 UIAbility实例间的状态共享。
工作原理:AppStorage与应用的进程绑定,由UI框架在应用程序启动时创建,当应用进程终止,AppStorage被回收。
优点:适用于需要在整个应用中-共享状态的场景。
注意事项:确保状态的生命周期与应用进程一致,避免在应用退出后仍有状态存在。
LocalStorage
适用场景:适用于单个Ability而言“全局”的变量,主要用于不同页面间的状态共享。
工作原理:LocalStorage的生命周期由应用程序决定,当应用释放最后一个指向 LocalStorage的引用时,LocalStorage被垃圾回收。
优点:适用于需要在单个UIAbility中不同页面间共享状态的场景。
注意事项:确保状态的生命周期与应用程序的生命周期一致,避免在应用退出后仍有状态存在。
11、LocalStorage在应用重启后数据会消失吗?
会,因为LocalStorage 是一种用于页面或组件级别的数据存储方式,它允许开发者在页面或组件的生命周期内存储和检索数据。LocalStorage 的数据存储在内存中,因此它的读写速度相对较快。但是,当应用重启后,LocalStorage 中的数据会丢失。
12、Navigation组件跳转和router跳转有什么区别?
- Navigation:是路由容器组件,适用于模块内和跨模块的路由切换,一次开发,多端部署场景。Router位于页面栈管理节点 stage 下面,不提供导航容器的概念。
- Navigation和 Router都支持跳转传参,但 Router对象中暂不支持方法变量。
- Navigation:支持清理指定路由,页面栈没有上限,可以无限跳转。Router不支持清理指定路由且页面栈最大为32,页面栈到达32之后必须清除之后才能继续跳转。
- Navigation:支持自定义转场动画和共享元素转场动画。 Router:仅支持简单自定义转场动画。
- Navigation:支持通过 setInterception 方法设置路由拦截。Router:不支持路由拦截。
- Navigation:支持沉浸式页面和模态嵌套路由。Router:不支持,需要通过窗口配置实现沉浸式页面。
总而言之,Navigation 组件在功能上更具丰富性和灵活性,特别是在处理复杂的导航结构、动效和路由管理方面。而 Router 则提供了更基础的路由跳转功能,适合简单的路由需求。开发者可以根据应用的具体需求和设计选择最合适的路由方案。
13、什么是Ability?
Ability是应用/服务所具备的能力的抽象,一个Module可以包含一个或多个 Ability ,在鸿蒙系统中,Ability提供了对 Ability生命周期、上下文环境等调用管理的能力,包括 Ability创建、销毁、转储客户端信息等
鸿蒙系统中的 Ability主要分为两种类型:UIAbility和 ExtensionAbility。
UIAbility :
- 定义 :包含UI界面,提供展示UI的能力,主要用于和用户交互 。
- 创建 :在模块中添加UIAbility时,选中对应的模块,单击鼠标右键,选择New > Ability,设置Ability名称,选择是否在设备主屏幕上显示该功能的启动图标,单击Finish完成Ability创建。
ExtensionAbility :
- 定义 :提供特定场景的扩展能力,满足更多的使用场景 。
- 创建 :在模块中添加ExtensionAbility时,选中对应的模块,单击鼠标右键,选择不同的场景类型(如Accessibility、EmbeddedUIExtensionAbility等)。当前仅Application工程支持创建ExtensionAbility。设置Ability名称,单击Finish完成ExtensionAbility创建。
此外,Ability是Ability模块的基类,提供系统配置更新回调和系统内存调整回调。Ability的继承关系包括UIAbility和ExtensionAbility等具体类。
总之,Ability是鸿蒙系统中用于管理应用能力的核心组件,通过不同类型的Ability可以实现不同的功能需求。
14、LazyForEach的工作原理是什么?
- LazyForEach 是一个用于高效渲染列表的组件或功能。
- 它允许开发者在用户滚动列表时才加载和渲染列表项,而不是一次性渲染整个列表。
- 这种按需渲染的方式可以显著提高应用的性能,特别是在处理大量数据时。
LazyForEach 的工作原理通常是基于用户的滚动位置来动态地创建和销毁列表项的组件实例。当用户滚动到列表的某个部分时,LazyForEach 会加载并渲染那些即将进入视图的列表项,同时可能会卸载那些滚出视图的列表项,以节省内存和计算资源。
15、什么是三层工程结构?一次开发,多端部署
common(公共能力层):用于存放公共基础能力集合(如工具库、公共配置等)。
common层可编译成一个或多个HAR包或HSP包(HAR中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝;而HSP中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份),其只可以被products和features依赖,不可以反向依赖。features(基础特性层):用于存放基础特性集合(如应用中相对独立的各个功能的UI及业务逻辑实现等)。
各个feature高内聚、低耦合、可定制,供产品灵活部署。不需要单独部署的feature通常编译为HAR包或HSP包,供products或其它feature使用,但是不能反向依赖products层。需要单独部署的feature通常编译为Feature类型的HAP包,和products下Entry类型的HAP包进行组合部署。features层可以横向调用及依赖common层。products(产品定制层):用于针对不同设备形态进行功能和特性集成。
products层各个子目录各自编译为一个Entry类型的HAP包,作为应用主入口。products层不可以横向调用。
16、如何优化应用的性能?
一、使用并行化、预加载和缓存等方法,提升系统资源利用率,减少主线程负载,加快应用的启动速度和响应速。
使用多线程执行耗时操作
使用异步执行耗时操作
使用预加载提升页面启动和响应速度
使用条件渲染实现预加载
使用缓存提升启动速度和滑动帧率
二、尽量减少布局的嵌套层数
移除冗余节点,删除无用的Stack/Column/Row嵌套
使用Column/Row替代Flex构建线性布局
使用Flex、List、Grid、RelativeContainer、绝对布局和自定义布局等构建复杂布局
三、合理管理状态变量
合理管理状态变量,减少不必要的参数层次传递
避免滥用@Provide+@Consume
控制对象级状态变量成员数量
避免不必要的创建和读取状态变量
四、合理使用系统接口,避免冗余操作
避免在系统高频调用进行冗余和耗时操作
避免在系统高频调用打印日志
在Release版本中删除Debug日志