Harmony面试题

收集官网faq:文档中心

一. ArkTS&ArkUI

1. 基础理论

  1. 鸿蒙相关的生命周期都有哪些?

UIAbility生命周期:onCreate、onWindowStageCreate、onForeground、onBackground、onWindowStageDestroy、onDestroy。

b2a4569047ea4771bf2a2da0ea8fd051.png

  • 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()回调中进行系统资源的释放、数据的保存等操作。

页面生命周期:onPageShow、onPageHide、onBackPress。

  • 页面生命周期,说白了就是@Entry修饰的组件,才称之为页面。

  • onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效。

  • onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效。

  • onBackPress:当用户点击返回按钮时触发,仅@Entry装饰的自定义组件生效。

组件生命周期: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开发范式

  • 我们来对比一下命令式和声明式

  • d3be00136c1145f8a684050f14fa226d.png
  • 声明式开发范式 : 只需要描述/声明 , 你要做什么 (通过封装好的组件以及相关熟悉方法 , 快速实现目的)

  • 命令式开发范式 : 不仅需要知道做什么 , 更需要知道如何做 (需要通过最原始的方式 , 一步一步实现)

  • 左侧是纯前端实现的按钮点击 , 也就是命令式

    • 命令式需要自己一点一点实现 , 如何做必须清楚

  • 右侧是ArkTs实现的按钮点击 , 也就是声明式

    • 声明式只需要知道做什么 , 调用对应api即可 , 不需要知道内部如何实现

 

3. 项目使用的是harmoneyos还是openharmoney,区别是啥

我们公司的项目使用的是HarmonyOS。

db1074a9e9844d23b3e8597b2e28620d.png

HarmonyOS:OpenHarmony+闭源应用和华为移动服务HMS(比如应用市场,视频,音乐等app)

Andoird:aosp(android open source project) + GMS(Google Mobile Service)

3. 关于context相关得内容有哪些 , 他们的区别?

各类Context的继承关系:

7aaf0f3b4c4044c9a50230ad723a9659.png

使用:

  1. page中获取上下文:通过函数getContext获取

    import common from '@ohos.app.ability.common';
    private context:common.UIAbilityContext = getContext(this) as common.UIAbilityContext
  2. Ability中获取上下文: 直接通过this获取context

    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        let uiAbilityContext = this.context;
        ...
      }

理论:

基类Context提供了获取应用文件路径的能力,ApplicationContext、AbilityStageContext、UIAbilityContext和ExtensionContext均继承该能力。应用文件路径属于应用沙箱路径,具体请参见应用沙箱目录

上述各类Context获取的应用文件路径有所不同。

通过ApplicationContext==获取应用级别的应用文件路径==,此路径是应用全局信息推荐的存放路径,这些文件会跟随应用的卸载而删除。

import common from '@ohos.app.ability.common';
@Entry
@Component
struct Page_Context { 
  private context = getContext(this) as common.UIAbilityContext; 
  build() {
    ...
    Button()
      .onClick(() => {
        let applicationContext = this.context.getApplicationContext();
        let cacheDir = applicationContext.cacheDir;//<路径前缀>/<加密等级>/base/cache
        let tempDir = applicationContext.tempDir;
        let filesDir = applicationContext.filesDir;
        let databaseDir = applicationContext.databaseDir;
        let bundleCodeDir = applicationContext.bundleCodeDir;
        let distributedFilesDir = applicationContext.distributedFilesDir;
        let preferencesDir = applicationContext.preferencesDir;
        // 获取应用文件路径
        let filePath = tempDir + 'test.txt';
        hilog.info(DOMAIN_NUMBER, TAG, `filePath: ${filePath}`);
        if (filePath !== null) {
          promptAction.showToast({
          message: filePath
          });
        }
      })
  }
}

通过AbilityStageContext、UIAbilityContext、ExtensionContext==获取HAP级别的应用文件路径==。此路径是HAP相关信息推荐的存放路径,这些文件会跟随HAP的卸载而删除,但不会影响应用级别路径的文件,除非该应用的HAP已全部卸载。

import common from '@ohos.app.ability.common'; 
@Entry
@Component
struct Page_Context { 
  private context = getContext(this) as common.UIAbilityContext; 
  build() {
    ...
    Button()
      .onClick(() => {
        let cacheDir = this.context.cacheDir;//<路径前缀>/<加密等级>/base/haps/<module-name>/cache
        let tempDir = this.context.tempDir;
        let filesDir = this.context.filesDir;
        let databaseDir = this.context.databaseDir;
        let bundleCodeDir = this.context.bundleCodeDir;
        let distributedFilesDir = this.context.distributedFilesDir;
        let preferencesDir = this.context.preferencesDir;
        // 获取应用文件路径
        let filePath = tempDir + 'test.txt'; 
      })
  }
}

 

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模型

  1. Stage模型 : HarmonyOS 3.1推出 也就是API9 , 是目前==主推==且会长期演进的模型

    • 由于提供了AbilityStage、WindowStage等类作为应用组件和Window窗口的“舞台”,因此称这种应用模型为Stage模型

    • stage: 舞台 /steɪdʒ/

  2. FA模型: FA(Feature Ability)模型:HarmonyOS早期版本开始支持的模型,已经不再主推

    • feature: 特点 /ˈfiːtʃə(r)/

  3. 区别: Stage模型与FA模型最大的区别在于

    1. Stage模型中,多个应用组件共享同一个ArkTS引擎实例;

    2. 而FA模型中,每个应用组件独享一个ArkTS引擎实例。

    3. 因此在Stage模型中,应用组件之间可以方便的共享对象和状态,同时减少复杂应用运行对内存的占用。

    4. 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

  1. @Provide和@Consume,用于祖先与后代组件的双向数据同步,实现跨层级传递

    • ==理解==:@Provide装饰器的变量是在祖先组件中,可以理解为被“提供”给后代的状态变量。@Consume装饰的变量是在后代组件中,去“消费”数据

  2. 语法特点:

    • @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

概述

  1. @Watch应用于==对状态变量的监听==。如果开发者需要关注某个状态变量的值是否改变,可以使用@Watch为状态变量设置回调函数。

  2. @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 })
    }
  }
}

 

 

3. 数据存储

1. LocalStorage和AppStorage的区别,和对应的装饰器以及PersistentStorage ***

LocalStorage

页面级UI状态存储,通常用于UIAbility内、页面间的状态共享。

localStorage是页面级数据存储,在页面中创建实例,组件中使用@LocalStorageLink和@LocalStorageProp装饰器修饰对应的状态变量,绑定对应的组件使用比状态属性更灵活

//应用逻辑使用LocalStorage
let para: Record<string,number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para); // 创建新实例并使用给定对象初始化 , 创建实例存储数据
let propA: number | undefined = storage.get('PropA') // propA == 47 ,get()获取数据
//link():如果给定的propName在LocalStorage实例中存在,则返回与LocalStorage中propName对应属性的双向绑定数据。 (双向同步)
let link1: SubscribedAbstractProperty<number> = storage.link('PropA'); // link1.get() == 47
let link2: SubscribedAbstractProperty<number> = storage.link('PropA'); // link2.get() == 47
//prop():如果给定的propName在LocalStorage中存在,则返回与LocalStorage中propName对应属性的单向绑定数据。  (单向同步)
let prop: SubscribedAbstractProperty<number> = storage.prop('PropA'); // prop.get() == 47
link1.set(48); // two-way sync: link1.get() == link2.get() == prop.get() == 48 //双向同步
prop.set(1); // one-way sync: prop.get() == 1; but link1.get() == link2.get() == 48 //单向同步
link1.set(49); // two-way sync: link1.get() == link2.get() == prop.get() == 49
  • new LocalStorage(数据Object)创建实例并存储数据

  • set方法,设置数据

  • get方法,获取数据

  • link方法,返回一个双向同步的变量

    • 与UI逻辑中@LocalStorageLink装饰器类似

  • prop方法,返回单向同步的变量

    • 与UI逻辑中@LocalStorageProp装饰器类似

//UI使用LocalStorage
//除了应用程序逻辑使用LocalStorage,还可以借助LocalStorage相关的两个装饰器@LocalStorageProp和@LocalStorageLink,在UI组件内部获取到LocalStorage实例中存储的状态变量。
// 创建新实例并使用给定对象初始化
let para: Record<string, number> = { 'PropA': 47 };
//这个变量一般就定义在某个@Entry组件的上方**
let storage: LocalStorage = new LocalStorage(para);
​
@Component
struct Child {
 // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
 @LocalStorageLink('PropA') storageLink2: number = 1;
​
 build() {
   Button(`Child from LocalStorage ${this.storageLink2}`)
     // 更改将同步至LocalStorage中的'PropA'以及Parent.storageLink1
     .onClick(() => {
       this.storageLink2 += 1
     })
 }
}
// 使LocalStorage可从@Component组件访问
@Entry(storage)//storage是上边定义的变量**
@Component
struct Parent {
 // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
 @LocalStorageLink('PropA') storageLink1: number = 1;
​
 build() {
   Column({ space: 15 }) {
     Button(`Parent from LocalStorage ${this.storageLink1}`) // initial value from LocalStorage will be 47, because 'PropA' initialized already
       .onClick(() => {
         this.storageLink1 += 1
     &
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值