鸿蒙开发的知识点,帮助快速记忆,持续更新记录......

384 阅读18分钟

管理组件拥有的状态,即Components级别的状态管理

  • @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的实例,应用于观察多层嵌套场景,和父组件的数据源构建双向同步。
  • 仅@Observed/@ObjectLink可以观察嵌套场景,其他的状态变量仅能观察第一层

其他状态管理(V1)

  • @Watch:用于监听状态变量的变化
  • $$运算符:给内置组件提供TS变量的引用,使得TS变量和内置组件的内部状态保持同步

装饰器总览(V2)

  • 状态管理(V2)提供了一套全新的装饰器
  • @ObservedV2:@ObservedV2装饰器装饰class,使得被装饰的class具有深度监听的能力。@ObservedV2和@Trace配合使用可以使class中的属性具有深度观测的能力。
  • @Trace:@Trace装饰器装饰被@ObservedV2装饰的class中的属性,被装饰的属性具有深度观测的能力。
  • @ComponentV2:使用@ComponentV2装饰的struct中能使用新的装饰器,例如:@Local,@Param,@Event,@Once,@Monitor,@Provider,@Consumer。
  • @Local:@Local装饰的变量为组件内部状态,无法从外部初始化。
  • @Param:@Param装饰的变量作为组件的输入,可以接受从外部传入初始化并同步。
  • @Once:@Once装饰的变量仅初始化时同步一次,需要与@Param一起使用。
  • @Event:@Event装饰方法类型,作为组件输出,可以通过该方法影响组件中的变量。
  • @Monitor:@Monitor装饰器用于@ComponentV2装饰的自定义组件或@ObservedV2装饰的类中,能够对状态变量继续深度监听。
  • @Provider和@Consumer:用于跨组件层级双向同步。
  • @Computed:计算属性,在被计算的值变化的时候,只会计算一次。主要应用于解决UI多次重用该属性从而重复计算导致的性能问题。
  • !!语法:双向绑定语法糖。

应用状态管理

  • LocalStorage:页面级UI状态存储,通常用于UIAbility内,页面间的状态共享。
  • AppStorage:特殊的单例LocalStorage对象,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。
  • PersistentStorage:持久化存储UI状态,通常和AppStorage配合使用,选择AppStorage存储的数据写入磁盘,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。
  • Enviroment:应用程序运行的设备的环境参数,环境参数会同步到AppStorage中,可以和AppStorage搭配使用。

MVVM架构模式

  • Model:负责存储和管理应用的数据以及业务逻辑,不直接与用户界面交互。通常从后端接口获取数据,是应用程序的数据基础,确保数据的一致性和完整性。
  • View:负责用户界面展示数据并与用户交互,不包含任何业务逻辑。它通过绑定ViewModel层提供的数据来动态更新UI。
  • ViewModel:负责管理UI状态和交互逻辑。作为连接Model和View的桥梁,ViewModel监控Model数据的变化,通知View更新UI,同时处理用户交互事件并转换为数据操作。

常用的装饰器

@Builder:@Builder装饰的函数也称为“自定义构建函数”

  • 自定义构建函数的参数传递有按值传递按引用传递两种,均需遵守以下规则:
  • 1,参数的类型必须与参数的声明类型一致,不允许undefined,null和返回undefined,null的表达式
  • 2,在@Builder修饰的函数内部,不允许改变参数值
  • 3,@Builder内UI语法遵循UI语法规则
  • 4,只有传入一个参数,且参数需要直接传入对象字面量才会按引用传递该参数,其余传递方式均为按值传递

@LocalBuilder:维持组件父子关系

  • 为了解决组件的父子关系和状态管理的父子关系保持一致的问题,引入@LocalBuilder装饰器。
  • @LocalBuilder拥有和局部@Builder相同的功能,且比局部@Builder能够更好的确定组件的父子关系和状态管理的父子关系。
  • 限制条件:
  • 1,@LocalBuilder只能在所属组件内声明,不允许全局声明
  • 2,@LocalBuilder不能被内置装饰器和自定义装饰器使用
  • 3,自定义组件内的静态方法不能和@LocalBuilder一起使用

@BuilderParam:引用@Builder函数

  • @BuilderParam用来装饰指向@Builder函数的变量
  • @BuilderParam装饰的变量接收来自父组件使用@Builder装饰的函数,且@Builder函数是参数传递类型,仅支持局部@Builder函数作为参数传递

wrapBuilder:封装全局@Builder

  • 当一个struct内部使用了多个全局@Builder函数,来实现UI的不同效果时,多个全局@Builder函数会使代码维护起来非常困难,并且页面不整洁。此时,可以使用wrapBuilder来封装全局@Builder
  • 限制条件:
  • 1,wrapBuilder方法只能传入全局@Builder函数
  • 2,wrapBuilder方法返回的WrappedBuilder对象的builder属性方法只能在struct内部使用

@Style:定义组件重用样式

  • @Style装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用
  • @Style仅支持通用属性和通用事件
  • @Style可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字
  • 限制条件:
  • 1,不支持在@Style方法内使用逻辑组件,在逻辑组件内的属性不生效
  • 2,@Style方法不能有参数,编译期会报错

@Extend:定义扩展组件样式

  • 和@Style不同,@Extend支持封装制定组件的私有属性,私有事件和自身定义的全局方法
  • 和@Style不同,@Extend装饰的方法支持参数
  • @Extend装饰的方法的参数可以为function,作为Event事件的句柄
  • @Extend的参数可以为状态变量,当状态变量改变时,UI可以正常的被刷新渲染
  • 限制条件:
  • 1,和@Style不同,@Extend仅支持在全局定义,不支持在组件内部定义。

stateStyles:多态样式

  • @Style仅仅应用于静态页面的样式复用,stateStyles可以依据组件内部状态的不同,快速设置不同的样式
  • stateStyles是属性方法,ArkUI提供五种状态:
  • focused:获焦态
  • normal:正常态
  • pressed:按压态
  • disabled:不可用态
  • selected:选中态

@AnimatableExtend:定义可动画属性

@Require:校验构造传参

  • @Require是校验@Prop,@State,@Provide,@BuilderParam和普通变量(无状态装饰器修饰的变量)是否需要构造传参的一个装饰器
  • 限制条件:
  • 1,@Require装饰器仅适用于装饰struct内的@Prop,@State,@Provide,@BuilderParam和普通变量

@Reusable:组件复用

  • @Reusable装饰器装饰任意自定义组件时,表示该自定义组件可以复用
  • 限制条件:
  • 1,@Reusable装饰器仅用于自定义组件
  • 2,ComponentContext不支持传入@Reusable装饰器装饰的自定义组件
  • 3,@Reusable装饰器不支持嵌套使用

知识点问答部分

HAR包是否支持依赖传递?

  • 问题现象:有三个HAR包分别为A,B,C;A依赖B,B依赖C。A是否可以引用C的资源?
  • 不支持A直接引用C的资源。A需要直接依赖C,即可引用。

跨模块如何跳转?

  • 采用HSP进行模块管理,可以实现页面之间的跳转,无需导入导包即可跳转,跳转方式如下:
  • 方式一:所有跳转到HSP内的页面需要使用特定的格式跳转:“@bundle:包名(bundleName)/模块名(moduleName)/路径/文件名”。(注意:不加.ets后缀)
  • 方式二:正常entry内模块路由跳转:“pages/文件名”。(注意:不加.ets后缀)
  • entry跳转到HSP页面:使用方式一
  • HSP跳转到entry页面:使用方式二
  • HSP跳转到HSP页面:使用方式一

UIAbility和UIExtensionAbility有什么区别?分别推荐在什么场景使用?

  • UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。UIAbility运行时,任务列表里会有对应的任务视图。
  • UIExtensionAbility组件是一种带UI的扩展组件。
  • UIExtensionAbility在运行期并没有独立的窗口,而是作为宿主的一个节点,嵌入到宿主窗口中显示,在任务列表中也没有对应的任务视图。
  • UIExtensionAbility是特定场景下带界面扩展能力的基类。不支持开发者直接继承该基类,但是开发者可以使用继承该类开发的其他一些ExtensionAbility,例如ShareExtensionAbility。

UIAbility/Page/Component之间的关系?

  • UIAbility是应用中的组件,一般建议一个HAP包中仅包含一个UIAbility。
  • Page是页面展示,一个UIAbility中可以有一个或多个Page,开发者可以根据系统提供的能力进行页面间跳转。
  • Component是页面组件的概念,例如Text组件,Button组件。一个Page里面可以有一个或多个Component,开发者通过不同的组件布局来实现不同的页面。

HAR包和HSP包的区别,动态包和静态包的区别,使用场景?

  • 作为用于实现代码和资源的共享。同一个Library类型的Module可以被其他的Module多次引用,合理地使用该类型的Module,能够降低开发和维护成本。
  • Library类型的Module分为Static和Shared两种类型,编译后会生成共享包。
  • Static Library:静态共享库。编译后会生成一个以.har为后缀的文件,即静态共享包HAR(Harmony Archive)。
  • Shared Library:动态共享库。编译后会生成一个以.hsp为后缀的文件,即动态共享包HSP(Harmony Shared Package)。
  • HAR和HSP两种共享包的主要区别体现在:

iShot_2024-12-18_22.41.32.png

har_hsp.png

  • 使用场景:HAR包适用于不同项目下代码和资源共享,可以上传至openharmony第三方共享仓开源,也可以在公司内搭建私有仓库进行共享。
  • HSP适用于单个项目内容三层架构按功能模块分包,有利于降低打包体积,目前HSP还不能项目共享。

HAP和HAR的区别是什么?

  • HAP和HAR都属于module。
  • HAP是包含Ability组件的module,能够独立运行,不会打入到别的模块中。
  • HAR是静态共享包module,不具备元能力,不能独立运行,同一个HAR在不同模块中使用时都会产生一份相同的编译产物。

对于HAP包中引用的HSP包是否有数量限制?

  • 目前没有数量限制。
  • 但是由于每个加载的HSP都需要占用一定的系统资源,过多的HSP包会对应用的性能造成影响。
  • 如果应用中HSP包数量过多,建议使用单HAP与多HAR方案,在动态加载场景中使用HSP。

app.json5文件与工程级build-profile.json5文件的区别?

  • 使用场景和优先级有所不同的。
  • app.json5文件主要用于应用的全局配置信息,包含应用的Bundle名称,开发厂商,版本号等基本信息。在应用中直接使用app.json5文件进行全局配置时,这些配置会被工程级build-profile.json5文件中的相同包名的配置所覆盖。
  • 工程局build-profile.json5文件主要用于定义构建和部署相关的配置。它包含了应用的包名,版本号等元数据,这些元数据可以在构建过程中被使用。如果app.json5文件中定义了与工程级build-profile.json5文件中相同包名的配置项,那么build-profile.json5文件中的该配置项会将app.json5文件中的配置项覆盖。
  • 建议以工程级build-profile.json5文件中的配置为主。因为在构建和部署过程中,系统通常会优先使用工程级build-profile.json5文件中的配置信息。

如何在HAP包中获取HSP的资源文件?

  • 方法一:通过资源管理获取HSP包下的资源文件。
  • 获取Context实例:UIAbility实例中的this.context,页面或组件中使用getContext()。使用HSP上下文的ResourceManager获取HSP目录下的资源内容。
  • 方法二:通过HSP包下实现一个资源管理类,以封装对外导出的资源。
  • 方法三:API12支持,$r('[hsp].media.xxxxxx')

UIAbility如何刷新ArkTS卡片?

  • 在卡片页面中可以通过postCardAction接口触发router事件或call事件拉起UIAbility,然后由UIAbility刷新卡片内容。

应用,元服务和卡片是什么关系?

  • 元服务也是一种应用,只不过没有图标,可以执行免安装拉起而已。
  • 应用和元服务,不能共享包名,他们得分开打包,元服务和应用之间是分割开的,也不能共享entry模块。
  • 应用与元服务都可以有卡片,而不是某一方特有的。
  • 元服务的卡片在手机上的入口,就表现为桌面卡片;在桌面长按任何一张已经添加的卡片,比如:图片和备忘录,在弹出来的菜单中选择“卡片中心”,进入卡片中心页面,可以找到卡片来添加到桌面。带下划线的桌面图标的应用表示存在服务卡片。

如何避免module下文件打包进HAR包后,存在的不可预期的资料、配置或信息安全风险?

  • 1,配置ohpmignore文件:若部分工程源文件无需构建到HAR包中,可在module目录下新建.ohpmignore文件,用于配置打包时要忽略的文件,将无需打包进HAR包的文件/文件夹名称写入.ohpmignore文件中。IDE构建时将过滤掉.ohpmignore文件中所包含的文件目录。
  • 需注意:更改.ohpmignore配置后,需要清空相应模块的build文件夹,或点击IDE的Build -> clean project后再打包。
  • 2,构建闭源HAR:IDE支持闭源HAR构建,通过对代码进行编译混淆,生成闭源HAR。在不共享源码的情况下,通过闭源HAR对外提供组件、资源等。可以实现多个模块或多个工程共享组件、资源等。
  • 开启方式:打卡模块级build-profile.json5文件,在obfuscation字段中配置混淆功能:enable是开启混淆。files是配置混淆规则文件路径。

iShot_2024-12-18_23.35.16.png

如何删除Record中的元素?

  • Record无直接修改的方式,可以转换成Map进行增删改后,再转换成Record。

@ObjectLink和@Observed什么场景下使用?

  • @ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:
  • 被@Observed装饰的类,可以被观察到属性的变化;
  • 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。
  • 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或@Prop使用。

你知道哪些提高ArkTS性能的写法?

  • 变量声明:
  • 1,使用const声明变量
  • 2,指定number的类型
  • 3,减少使用ESObject
  • 属性访问:
  • 1,减少变量的属性查找
  • 2,给类属性添加访问修饰符
  • 数值计算:
  • 1,数值计算使用TypedArray
  • 数据结构的使用:
  • 1,选取合适的数据结构
  • 2,避免造成稀疏数组
  • 函数声明与使用:
  • 1,函数内部变量尽量使用参数传递,不需要闭包

@Require作用?

  • @Require是校验@Prop和@BuilderParam是否需要构造传参的一个装饰器,@Require装饰器不能单独使用。
  • 当Child组件内使用@Require装饰器和@Prop或者@BuilderParam结合使用时,父组件Index在构造Child时必须传参,否则编译不通过。

H5页面如何与ArkTS交互?

  • 通过javaScriptProxy和runJavaScript封装,实现JSBridge通信方案。
  • 使用Web组件javaScriptProxy将原生侧接口注入到H5的window对象上,通过runJavaScript接口执行JS脚本到H5中,并在回调中获取脚本执行结果。

有哪些创建线程的方式?

  • 在ArkTS中使用Worker创建线程:Worker线程在主线程中创建,与主线程相互独立,但不能直接操作UI,最多可以创建8个Worker线程。
  • 在ArkTS中使用任务池(TaskPool)创建线程任务。
  • 在C代码中使用标准的线程API创建线程。

线程或进程数据通信方式?

  • eventHub:普通事件发布,订阅
    • eventHub:提供了事件中心,提供订阅,取消订阅,触发事件的能力,同hap内通信,不跨线程
    • eventHub.emit(数据标记,数据),触发事件
    • eventHub.on(数据标记,()=>{}),监听事件
  • emitter:处理进程内,线程间事件,发送事件会放到事件队列,多hap通信方案
    • emitter.emit({eventId:xx},数据),触发事件
    • emitter.on({eventId:xx},()=>{}),监听事件
  • worker:处理线程间事件,主要处理耗时事件
  • 跨进程commonEventmanager

Web组件的onLoadIntercept返回结构是否影响onInterceptReques?

  • Web组件的onLoadIntercept的不同返回结果对应不同的操作:
    • onLoadIntercept返回true则直接拦截URL请求;
    • onLoadIntercept返回false则走onInterceptReques回调。

常用的修饰符有哪些?

  • State,Prop,Link,Provide,Consume,Observed,ObjectLink,Watch,StorageProp,StorageLink,LocalStorageProp, LocalStorageLink,Builder,BuilderParams,Style,Extends,Require,Entry,Component,CustomDialog

Promise如何使用?会不会阻塞UI线程?

  • Promise是JavaScript中用于处理异步操作的一种机制,它提供了更优雅的方式来处理异步代码,避免了回调地狱。
    • 1,使用new Promise()创建Promise对象
    • 2,处理成功和失败,在执行器函数中进行异步操作,根据结果调用resolve或reject
    • 3,使用then和catch处理结果,使用then处理成功的情况,使用catch处理失败的情况
  • Promise不会阻塞UI线程,Promise设计的目的之一就是避免阻塞主线程。
  • 异步操作的执行不会阻塞UI线程,而是通过事件循环机制在后台执行。
  • 当异步操作完成后,它会调用相应的resolve或reject,然后触发注册的then或catch处理程序。

组件通信方式有哪些?

  • 可以通过 @Prop 修饰符传基本的字符串/数字/布尔值,父传子
  • 子传父:父给子传一个函数,子调用父的函数(注意this的问题)
  • @Link子更新Link,父 @Watch 这个state
  • 父更新state,子watch link
  • @Provide/@Consume
  • 事件总线:emitter/context.eventhub.on/emit

import依赖树较大如何优化?

  • 可以动态加载,解决依赖过大的问题
  • ArkCompiler支持动态import方法,支持运行时阶段动态加载模块
  • 通过import()接收一个模块路径作为参数,返回一个Promise对象;当Promise对象被resolve时,使用then()方法获取模块的默认导出。
  • 可参考如下示例代码:
    try {
       //加载所需模块
       import('./Index').then(module => {
           //在需要时使用所需模块的代码
           //...
       })
    } catch (error) {
        //...
    }

如何使用ohpm引入三方库?

  • 打开终端窗口,通过指令进入entry目录,在对应的文件中直接引用
    • ohpm install test
    • import test from ‘test’
  • 打开工程目录下的entry目录,找到该目录下的oh-package.json5文件
    • 在oh-package.json5文件中写入想安装的三方库

TaskPool开启子线程和Promise的区别?

  • TaskPool:是多线程异步,是通过调用线程池内的线程去异步完成任务;
  • Promise:是同一个线程在各个任务间进行切换,通过暂时挂起任务实现异步,异步代码会被挂起并在之后继续执行,并且同一时间只有一段代码执行。