鴻蒙面試題庫(kù)收集(二):ArkTS&ArkUI數(shù)據(jù)存儲(chǔ)&組件&布局

3. 數(shù)據(jù)存儲(chǔ)

1. LocalStorage和AppStorage的區(qū)別,和對(duì)應(yīng)的裝飾器以及PersistentStorage ***

LocalStorage

頁(yè)面級(jí)UI狀態(tài)存儲(chǔ)园匹,通常用于UIAbility內(nèi)蝙叛、頁(yè)面間的狀態(tài)共享。

localStorage是頁(yè)面級(jí)數(shù)據(jù)存儲(chǔ)份乒,在頁(yè)面中創(chuàng)建實(shí)例半沽,組件中使用@LocalStorageLink和@LocalStorageProp裝飾器修飾對(duì)應(yīng)的狀態(tài)變量砂代,綁定對(duì)應(yīng)的組件使用比狀態(tài)屬性更靈活

//應(yīng)用邏輯使用LocalStorage
let para: Record<string,number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para); // 創(chuàng)建新實(shí)例并使用給定對(duì)象初始化 惜互, 創(chuàng)建實(shí)例存儲(chǔ)數(shù)據(jù)
let propA: number | undefined = storage.get('PropA') // propA == 47 布讹,get()獲取數(shù)據(jù)
//link():如果給定的propName在LocalStorage實(shí)例中存在,則返回與LocalStorage中propName對(duì)應(yīng)屬性的雙向綁定數(shù)據(jù)训堆。 (雙向同步)
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對(duì)應(yīng)屬性的單向綁定數(shù)據(jù)。  (單向同步)
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(數(shù)據(jù)Object)創(chuàng)建實(shí)例并存儲(chǔ)數(shù)據(jù)

  • set方法坑鱼,設(shè)置數(shù)據(jù)

  • get方法膘流,獲取數(shù)據(jù)

  • link方法,返回一個(gè)雙向同步的變量

    • 與UI邏輯中@LocalStorageLink裝飾器類似
  • prop方法鲁沥,返回單向同步的變量

    • 與UI邏輯中@LocalStorageProp裝飾器類似
//UI使用LocalStorage
//除了應(yīng)用程序邏輯使用LocalStorage呼股,還可以借助LocalStorage相關(guān)的兩個(gè)裝飾器@LocalStorageProp和@LocalStorageLink,在UI組件內(nèi)部獲取到LocalStorage實(shí)例中存儲(chǔ)的狀態(tài)變量画恰。
// 創(chuàng)建新實(shí)例并使用給定對(duì)象初始化
let para: Record<string, number> = { 'PropA': 47 };
//這個(gè)變量一般就定義在某個(gè)@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
       })
     // @Component子組件自動(dòng)獲得對(duì)CompA LocalStorage實(shí)例的訪問權(quán)限彭谁。
     Child()
   }
 }
}
//上述代碼:如果將LocalStorageLink改為L(zhǎng)ocalStorageProp就由雙向變?yōu)榱藛蜗?
  • new LocalStorage(數(shù)據(jù)Object)創(chuàng)建實(shí)例并存儲(chǔ)數(shù)據(jù)
  • 父組件:
    • @Entry(LocalStorage實(shí)例) , 將LocalStorage數(shù)據(jù)注冊(cè)到頁(yè)面中阐枣,使得頁(yè)面內(nèi)部可以使用數(shù)據(jù)
    • @LocalStorageLink 马靠,將頁(yè)面變量與數(shù)據(jù)進(jìn)行雙向綁定,父子組件都可以使用
    • @LocalStorageProp蔼两,將頁(yè)面變量與數(shù)據(jù)進(jìn)行單向綁定,父子組件都可以使用
AppStorage

AppStorage是進(jìn)程級(jí)數(shù)據(jù)存儲(chǔ)(==應(yīng)用級(jí)的全局狀態(tài)共享==)逞度,進(jìn)程啟動(dòng)時(shí)自動(dòng)創(chuàng)建了唯一實(shí)例额划,在各個(gè)頁(yè)面組件中@StorageProp和@StorageLink裝飾器修飾對(duì)應(yīng)的狀態(tài)變量。

//AppStorage是單例档泽,它的所有API都是靜態(tài)的
AppStorage.setOrCreate('PropA', 47);

let propA: number | undefined = AppStorage.get('PropA') // propA in AppStorage == 47
let link1: SubscribedAbstractProperty<number> = AppStorage.link('PropA'); // link1.get() == 47
let link2: SubscribedAbstractProperty<number> = AppStorage.link('PropA'); // link2.get() == 47
let prop: SubscribedAbstractProperty<number> = AppStorage.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

AppStorage.get<number>('PropA') // == 49
link1.get() // == 49
link2.get() // == 49
prop.get() // == 49
  • 語(yǔ)法基本上與LocalStorage類似俊戳,只不過是靜態(tài)方法
  • link雙向,prop單向
AppStorage.setOrCreate('PropA', 47);

@Entry(storage)
@Component
struct CompA {
  @StorageLink('PropA') storageLink: number = 1;

  build() {
    Column({ space: 20 }) {
      Text(`From AppStorage ${this.storageLink}`)
        .onClick(() => {
          this.storageLink += 1
        })
    }
  }
}
  • @StorageLink換為@StorageProp馆匿,雙向就變?yōu)閱蜗虻牧?/li>
  • 不建議使用@StorageLink 抑胎, 其實(shí)就是因?yàn)锳ppStorage共享范圍太多,更新效率低下渐北,也可能造成不必要的更新

補(bǔ)充:

localStorage和appStorage數(shù)據(jù)存取都是在主線程進(jìn)行的阿逃,且api只提供了同步接口,存取數(shù)據(jù)時(shí)要注意數(shù)據(jù)的大小。

  • 關(guān)于存儲(chǔ)時(shí)數(shù)據(jù)的大小問題
    • AppStorage沒有大小限制恃锉,單條數(shù)據(jù)[k,v)],v也沒有限制搀菩,但是不建議單條v大于1kb,大于1kb建議使用數(shù)據(jù)庫(kù)。多條使用沒有限制破托,會(huì)動(dòng)態(tài)分配的肪跋。
    • LocalStorage底層實(shí)現(xiàn)是一個(gè)map,理論上沒有大小限制。
PersistentStorage

PersistentStorage將選定的AppStorage屬性保留在設(shè)備磁盤上土砂。應(yīng)用程序通過API州既,以決定哪些AppStorage屬性應(yīng)借助PersistentStorage持久化。UI和業(yè)務(wù)邏輯不直接訪問PersistentStorage中的屬性萝映,所有屬性訪問都是對(duì)AppStorage的訪問吴叶,AppStorage中的更改會(huì)自動(dòng)同步到PersistentStorage。

PersistentStorage和AppStorage中的屬性建立雙向同步锌俱。應(yīng)用開發(fā)通常通過AppStorage訪問PersistentStorage晤郑,另外還有一些接口可以用于管理持久化屬性,但是業(yè)務(wù)邏輯始終是通過AppStorage獲取和設(shè)置屬性的

從AppStorage中訪問PersistentStorage初始化的屬性

  1. 初始化PersistentStorage:

    PersistentStorage.persistProp('aProp', 47);
    
  2. 在AppStorage獲取對(duì)應(yīng)屬性:

    AppStorage.get<number>('aProp'); // returns 47
    

    或在組件內(nèi)部定義:

    @StorageLink('aProp') aProp: number = 48;
    

2.數(shù)據(jù)存儲(chǔ)怎么存贸宏?都用過什么數(shù)據(jù)存儲(chǔ)造寝?用戶首選項(xiàng),鍵值型數(shù)據(jù)庫(kù)吭练,關(guān)系數(shù)據(jù)庫(kù)

首選項(xiàng)

用戶首選項(xiàng)(Preferences):提供了==輕量級(jí)配置數(shù)據(jù)的持久化能力==诫龙,并支持訂閱數(shù)據(jù)變化的通知能力。不支持分布式同步鲫咽,常用于保存==應(yīng)用配置信息签赃、用戶偏好設(shè)置==等。
約束限制:

  • Key鍵為string類型分尸,要求非空且長(zhǎng)度不超過==80個(gè)字節(jié)==
  • 如果Value值為string類型锦聊,請(qǐng)使用UTF-8編碼格式,可以為空箩绍,不為空時(shí)長(zhǎng)度不超過==8192個(gè)字節(jié)==
  • 內(nèi)存會(huì)隨著存儲(chǔ)數(shù)據(jù)量的增大而增大孔庭,所以存儲(chǔ)的數(shù)據(jù)量應(yīng)該是輕量級(jí)的,建議存儲(chǔ)的數(shù)據(jù)==不超過一萬(wàn)條==材蛛,否則會(huì)在內(nèi)存方面產(chǎn)生較大的開銷

代碼:

import dataPreferences from '@ohos.data.preferences';
//1. 獲取preference
private preferences: dataPreferences.Preferences =  dataPreferences.getPreferencesSync(this.context, { name: 'myStore' });

//2. 保存數(shù)據(jù)
this.preferences.putSync('key', value);
//3. 持久化數(shù)據(jù)
this.preferences.flush()
//4. 獲取數(shù)據(jù)
let result = this.preferences.getSync("key",16)
this.changeFontSize = Number(result)
鍵值型數(shù)據(jù)庫(kù)

鍵值型數(shù)據(jù)庫(kù)存儲(chǔ)鍵值對(duì)形式的數(shù)據(jù)圆到,當(dāng)需要存儲(chǔ)的數(shù)據(jù)沒有復(fù)雜的關(guān)系模型,比如存儲(chǔ)商品名稱及對(duì)應(yīng)價(jià)格卑吭、員工工號(hào)及今日是否已出勤等芽淡,由于數(shù)據(jù)復(fù)雜度低,更容易兼容不同數(shù)據(jù)庫(kù)版本和設(shè)備類型豆赏,因此推薦使用鍵值型數(shù)據(jù)庫(kù)持久化此類數(shù)據(jù)挣菲。

約束:

  • 設(shè)備協(xié)同數(shù)據(jù)庫(kù)富稻,針對(duì)每條記錄,Key的長(zhǎng)度≤896 Byte己单,Value的長(zhǎng)度<4 MB唉窃。

  • 單版本數(shù)據(jù)庫(kù),針對(duì)每條記錄纹笼,Key的長(zhǎng)度≤1 KB纹份,Value的長(zhǎng)度<4 MB。

  • 每個(gè)應(yīng)用程序最多支持同時(shí)打開16個(gè)鍵值型分布式數(shù)據(jù)庫(kù)廷痘。

  • 鍵值型數(shù)據(jù)庫(kù)事件回調(diào)方法中不允許進(jìn)行阻塞操作蔓涧,例如修改UI組件。

剩余內(nèi)容:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/data-persistence-by-kv-store-0000001820999693

關(guān)系型數(shù)據(jù)庫(kù)

關(guān)系型數(shù)據(jù)庫(kù)基于SQLite組件笋额,適用于存儲(chǔ)包含==復(fù)雜關(guān)系數(shù)據(jù)==的場(chǎng)景元暴,比如一個(gè)班級(jí)的學(xué)生信息,需要包括姓名兄猩、學(xué)號(hào)茉盏、各科成績(jī)等,又或者公司的雇員信息枢冤,需要包括姓名鸠姨、工號(hào)、職位等淹真,由于數(shù)據(jù)之間有較強(qiáng)的對(duì)應(yīng)關(guān)系讶迁,復(fù)雜程度比鍵值型數(shù)據(jù)更高,此時(shí)需要使用關(guān)系型數(shù)據(jù)庫(kù)來(lái)持久化保存數(shù)據(jù)核蘸。

運(yùn)作機(jī)制:

<img src="0.Harmony面試題.assets/image-20240521094339897.png" alt="image-20240521094339897" style="zoom: 33%;" />

約束:

  • 數(shù)據(jù)庫(kù)中有4個(gè)讀連接和1個(gè)寫連接巍糯,線程獲取到空閑讀連接時(shí),即可進(jìn)行讀取操作客扎。當(dāng)沒有空閑讀連接且有空閑寫連接時(shí)祟峦,會(huì)將寫連接當(dāng)做讀連接來(lái)使用。
  • 為保證數(shù)據(jù)的準(zhǔn)確性徙鱼,數(shù)據(jù)庫(kù)同一時(shí)間只能支持一個(gè)寫操作搀愧。
  • 當(dāng)應(yīng)用被卸載完成后,設(shè)備上的相關(guān)數(shù)據(jù)庫(kù)文件及臨時(shí)文件會(huì)被自動(dòng)清除疆偿。
  • 為保證插入并讀取數(shù)據(jù)成功,建議一條數(shù)據(jù)不要超過2M搓幌。超出該大小杆故,插入成功,讀取失敗溉愁。

代碼:

import relationalStore from '@ohos.data.relationalStore'
import { common } from '@kit.AbilityKit'

export  class DBUtils {
  // 數(shù)據(jù)庫(kù)名稱
  private tableName: string = 'accountTable'
  // 建表語(yǔ)句
  private sqlCreate: string = 'CREATE TABLE IF NOT EXISTS accountTable(id INTEGER PRIMARY KEY AUTOINCREMENT, accountType INTEGER, ' +
    'typeText TEXT, amount INTEGER)'
  // 表字段
  private columns: string[] = ['id', 'accountType', 'typeText', 'amount']
  // 數(shù)據(jù)庫(kù)核心類
  private rdbStore: relationalStore.RdbStore | null = null
  // 數(shù)據(jù)庫(kù)配置
  DB_CONFIG: relationalStore.StoreConfig = {
    name: 'RdbTest.db', // 數(shù)據(jù)庫(kù)文件名
    securityLevel: relationalStore.SecurityLevel.S1, // 數(shù)據(jù)庫(kù)安全級(jí)別
  };

  /**
   * 獲取rdb
   * @param context:上下文
   * @param callback:回調(diào)函數(shù)处铛,我們第一次獲取數(shù)據(jù)時(shí)饲趋,需要在獲取到rdb之后才能獲取,所以有此回調(diào)
   */
  getRdbStore(context: common.UIAbilityContext, callback: Function) {
    relationalStore.getRdbStore(context, this.DB_CONFIG, (error, store) => {
      if (this.rdbStore !== null) {
        //如果已經(jīng)有rdb撤蟆,直接建表
        store.executeSql(this.sqlCreate)
        return
      }
      //保存rdb奕塑,下邊會(huì)用
      this.rdbStore = store
      //建表
      store.executeSql(this.sqlCreate)
      console.log("test", "successed get dbStore")
      if (callback) callback()
    })
  }

  /**
   * 插入數(shù)據(jù)
   * @param data:數(shù)據(jù)對(duì)象
   * @param callback:回調(diào)函數(shù),這里的結(jié)果是通過回調(diào)函數(shù)返回的(也可使用返回值)
   */
  insertData(data: AccountData, callback: Function) {
    //將數(shù)據(jù)對(duì)象家肯,轉(zhuǎn)換為ValuesBucket類型
    const valueBucket: relationalStore.ValuesBucket = generateBucket(data);
    // 調(diào)用insert插入數(shù)據(jù)
    this.rdbStore && this.rdbStore.insert(this.tableName, valueBucket, (err, res) => {
      if (err) {
        console.log("test龄砰,插入失敗", err)
        callback(-1)
        return
      }
      console.log("test,插入成功", res)
      callback(res) //res為行號(hào)
    })

  }

  /**
   * 獲取數(shù)據(jù)
   * @param callback:接收結(jié)果的回調(diào)函數(shù)
   */
  query(callback: Function) {
    //predicates是用于添加查詢條件的
    let predicates = new relationalStore.RdbPredicates(this.tableName)
    // 查詢所有讨衣,不需要條件
    // predicates.equalTo("字段",數(shù)據(jù))
    this.rdbStore && this.rdbStore.query(predicates, this.columns, (error, resultSet: relationalStore.ResultSet) => {
      if(error){
        console.log("test,獲取數(shù)據(jù)失敗",JSON.stringify(error))
        return
      }
      let count: number = resultSet.rowCount
      console.log("test","數(shù)據(jù)庫(kù)中數(shù)據(jù)數(shù)量:"+count) //沒數(shù)據(jù)時(shí)返回-1或0
      if (count <= 0 || typeof count === 'string') {
        callback([])
        return
      }
      let result: AccountData[] = []
      //上來(lái)必須調(diào)用一次goToNextRow换棚,讓游標(biāo)處于第一條數(shù)據(jù),while(resultSet.goToNextRow())是最有寫法
      while(resultSet.goToNextRow()) {
        let accountData:AccountData = {id:0,accountType:0,typeText:'',amount:0}
        accountData.id = resultSet.getDouble(resultSet.getColumnIndex('id'));
        accountData.typeText = resultSet.getString(resultSet.getColumnIndex('typeText'))
        accountData.accountType = resultSet.getDouble(resultSet.getColumnIndex('accountType'))
        accountData.amount = resultSet.getDouble(resultSet.getColumnIndex('amount'))
        result.push(accountData)
      }
      callback(result)
      resultSet.close()//釋放數(shù)據(jù)集內(nèi)容
    })
  }
}
function generateBucket(account: AccountData): relationalStore.ValuesBucket {
  let obj: relationalStore.ValuesBucket = {};
  obj.accountType = account.accountType;
  obj.typeText = account.typeText;
  obj.amount = account.amount;
  return obj;
}

export class AccountData {
  id: number = -1;
  accountType: number = 0;
  typeText: string = '';
  amount: number = 0;
}

使用代碼:(部分代碼反镇,從HelloDataManager中copy的)

// 數(shù)據(jù)庫(kù)工具類
  private dbUitls: DBUtils = new DBUtils()

// 界面打開時(shí)固蚤,查詢數(shù)據(jù),展示胡靜
  aboutToAppear(): void {
    this.dbUitls.getRdbStore(this.context, () => {
      this.queryData()
    })
  }
  // 查詢數(shù)據(jù)方法
  queryData(){
    this.dbUitls.query((result: AccountData[]) => {
      this.accountDataArray = result
      console.log("test歹茶,獲取數(shù)據(jù)成功:", JSON.stringify(this.accountDataArray))
    })
  }
  // 點(diǎn)擊確定回調(diào)
  onConfirm(insertData: AccountData) {
    console.log("test", JSON.stringify(insertData))
    // 插入數(shù)據(jù)
    this.dbUitls.insertData(insertData, (res: number) => {
      if (res > 0) {
        // AlertDialog.show({ message: "添加成功" })
        this.queryData()
      } else {
        AlertDialog.show({ message: "添加失敗" })
      }
    })
  }

4. 組件&布局

1.用過flex布局嗎夕玩?flex存在的問題是什么?為什么會(huì)造成二次渲染惊豺?如何優(yōu)化燎孟?

  • 用過flex

    • 彈性布局(Flex)提供更加有效的方式對(duì)容器中的子元素進(jìn)行排列、對(duì)齊和分配剩余空間扮叨。常用于頁(yè)面頭部導(dǎo)航欄的均勻分布缤弦、頁(yè)面框架的搭建、多行數(shù)據(jù)的排列等彻磁。

      容器默認(rèn)存在主軸與交叉軸碍沐,子元素默認(rèn)沿主軸排列,子元素在主軸方向的尺寸稱為主軸尺寸衷蜓,在交叉軸方向的尺寸稱為交叉軸尺寸累提。

  • flex會(huì)造成二次渲染

    • flex中flexgrow=1時(shí),子組件寬度和大于flex的寬度時(shí)磁浇,頁(yè)面渲染后斋陪,會(huì)調(diào)整子組件寬度使之寬度和等于flex的寬度,造成二次布局渲染
      flex中flexshrink=1時(shí)置吓,子組件寬度和小于flex的寬度時(shí)无虚,頁(yè)面渲染后,也會(huì)調(diào)整子組件寬度使之寬度和等于flex的寬度衍锚,造成二次布局渲染

    • flexGrow:設(shè)置父容器的剩余空間分配給此屬性所在組件的比例友题。用于分配父組件的剩余空間。

      flexShrink: 當(dāng)父容器空間不足時(shí)戴质,子元素的壓縮比例度宦。

      參考鏈接:https://www.seaxiang.com/blog/8bf810f30c9f4fe7a24fb55c4778ed6c

  • 優(yōu)化

    • 使用Column/Row代替Flex踢匣。
    • 大小不需要變更的子組件主動(dòng)設(shè)置flexShrink屬性值為0。
    • 優(yōu)先使用layoutWeight屬性替代flexGrow屬性和flexShrink屬性戈抄。
    • 子組件主軸長(zhǎng)度分配設(shè)置為最常用場(chǎng)景的布局結(jié)果离唬,使子組件主軸長(zhǎng)度總和等于Flex容器主軸長(zhǎng)度。
  • 參考鏈接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-layout-development-flex-layout-0000001774120126#ZH-CN_TOPIC_0000001774120126__%E6%A6%82%E8%BF%B0

2. flex導(dǎo)致二次布局的原因划鸽,以及調(diào)研的經(jīng)歷

flex中flexgrow=1時(shí)输莺,子組件寬度和大于flex的寬度時(shí),頁(yè)面渲染后漾稀,會(huì)調(diào)整子組件寬度使之寬度和等于flex的寬度模闲,造成二次布局渲染
flex中flexshrink=1時(shí),子組件寬度和小于flex的寬度時(shí)崭捍,頁(yè)面渲染后尸折,也會(huì)調(diào)整子組件寬度使之寬度和等于flex的寬度,造成二次布局渲染

3.使用了哪些組件殷蛇,有沒有寫過自定義組件实夹,自定義組件怎么設(shè)計(jì)的

  • Button是按鈕組件,通常用于響應(yīng)用戶的點(diǎn)擊操作粒梦,其類型包括膠囊按鈕亮航、圓形按鈕、普通按鈕匀们。Button做為容器使用時(shí)可以通過添加子組件實(shí)現(xiàn)包含文字缴淋、圖片等元素的按鈕。

    Text是文本組件泄朴,通常用于展示用戶視圖重抖,如顯示文章的文字

  • 寫過自定義組件

    • struct:自定義組件基于struct實(shí)現(xiàn),struct + 自定義組件名 + {...}的組合構(gòu)成自定義組件祖灰,不能有繼承關(guān)系钟沛。對(duì)于struct的實(shí)例化,可以省略new局扶。
    • @Component:@Component裝飾器僅能裝飾struct關(guān)鍵字聲明的數(shù)據(jù)結(jié)構(gòu)恨统。struct被@Component裝飾后具備組件化的能力,需要實(shí)現(xiàn)build方法描述UI三妈,一個(gè)struct只能被一個(gè)@Component裝飾畜埋。@Component可以接受一個(gè)可選的bool類型參數(shù)。
    • build()函數(shù):build()函數(shù)用于定義自定義組件的聲明式UI描述畴蒲,自定義組件必須定義build()函數(shù)由捎。
    • @Entry:@Entry裝飾的自定義組件將作為UI頁(yè)面的入口。在單個(gè)UI頁(yè)面中饿凛,最多可以使用@Entry裝飾一個(gè)自定義組件狞玛。@Entry可以接受一個(gè)可選的LocalStorage的參數(shù)。
    • @Reusable:@Reusable裝飾的自定義組件具備可復(fù)用能力
    • 參考鏈接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-create-custom-components-0000001820999549#ZH-CN_TOPIC_0000001820999549__%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95
  • 自定義組件怎么設(shè)計(jì),有兩種情況

    1. 首先公司內(nèi)部將常用的一些效果 , 都封裝了組件
      1. 比如: listView下拉刷新動(dòng)畫 , 進(jìn)度條等等
      2. 具體自定義組件如何定義 , 參考上述鏈接
    2. 其次就是一個(gè)應(yīng)用中 , 如果一個(gè)布局在多個(gè)頁(yè)面中出現(xiàn) , 我們就可以將這個(gè)布局效果封裝為一個(gè)組件
      1. 比如: 頁(yè)面的頭部標(biāo)題 , 點(diǎn)贊 , 收藏等
      2. 具體自定義組件如何定義 , 參考上述鏈接

4. 自定義組件,實(shí)際開發(fā)中封裝過哪些自定義組件

例如:下拉刷新涧窒、自定義彈窗心肪、自定義LoadingProgress、統(tǒng)一標(biāo)題欄的封裝等...

自定義組件具有以下特點(diǎn):

  • 可組合:允許組合使用系統(tǒng)組件纠吴、及其屬性和方法硬鞍。
  • 可重用:自定義組件可以被其他組件重用,并作為不同的實(shí)例在不同的父組件或容器中使用戴已。
  • 數(shù)據(jù)驅(qū)動(dòng)UI更新:通過狀態(tài)變量的改變固该,來(lái)驅(qū)動(dòng)UI的刷新。

自定義組件基于struct實(shí)現(xiàn)糖儡,struct + 自定義組件名 + {...}的組合構(gòu)成自定義組件伐坏,不能有繼承關(guān)系。

struct被@Component裝飾后具備組件化的能力握联,需要實(shí)現(xiàn)build方法描述UI桦沉,一個(gè)struct只能被一個(gè)@Component裝飾。

5.項(xiàng)目中用到arkTs哪些技術(shù)

arkTs的技術(shù)金闽?
裝飾器
渲染控制:

6.flex-shrink的使用

答: 設(shè)置父容器壓縮尺寸分配給此屬性所在組件的比例, 設(shè)置為==0表示不壓縮==,
父容器為Row纯露、Column時(shí),默認(rèn)值:0代芜。
父容器為flex時(shí)埠褪,默認(rèn)值:1。
在子組件的總寬度大于父容器的寬度時(shí)挤庇,子組件中有設(shè)置flex-shrink , 并且值大于0時(shí)钞速,父容器會(huì)把剩余寬度按照比例壓縮子組件。
Flex布局-通用屬性-組件通用信息-組件參考(基于ArkTS的聲明式開發(fā)范式)-ArkTS API參考-HarmonyOS應(yīng)用開發(fā)

關(guān)于flexShrink如何計(jì)算罚随,參考:http://www.reibang.com/p/f64eb6613d35
自己總結(jié)公式:
溢出值:所有子元素寬度相加 - 父元素寬度
總權(quán)重:子元素1的flexShrink*子元素1寬度 + 子元素2的flexShrink*子元素2寬度 +...
子元素壓縮值:溢出值 * 子元素的權(quán)重 = 溢出值 * 子元素1的flexShrink*子元素1寬度/總權(quán)重

7.webview組件如何使用玉工,ets文件如何與h5通訊 ***

總結(jié):兩種方式:
方式一:

runJavaScript() arkts-》H5 
javaScriptProxy() 和 registerjavaScriptProxy() H5-》arkts

方式二:

  createWebMessagePorts postMessage onMessageEvent 數(shù)據(jù)通道

這里的方案是,相互調(diào)用函數(shù)來(lái)通信淘菩,12題是通過消息端口建立數(shù)據(jù)通道

  1. @ohos.web.webview提供web控制能力遵班,web組件提供網(wǎng)頁(yè)顯示的能力

  2. webview組件如何使用?

    1. 添加網(wǎng)絡(luò)權(quán)限: ohos.permission.INTERNET
    2. 加載網(wǎng)頁(yè)
      // xxx.ets
      import web_webview from '@ohos.web.webview'
      
      @Entry
      @Component
      struct WebComponent {
      controller: web_webview.WebviewController = new web_webview.WebviewController()
      build() {
        Column() {
          //加載在線網(wǎng)頁(yè)
          Web({ src: 'www.example.com', controller: this.controller })
          //加載本地網(wǎng)頁(yè)
          // 通過$rawfile加載本地資源文件。
          Web({ src: $rawfile("index.html"), controller: this.controller })
          // 通過resource協(xié)議加載本地資源文件潮改。
          Web({ src: "resource://rawfile/index.html", controller: this.controller })
        }
      }
      }
      
  3. ets文件如何與h5通訊?

    1. ets調(diào)用h5網(wǎng)頁(yè)方法

      1. h5定義方法
        <!-- index.html -->
        <!DOCTYPE html>
        <html>
        <meta charset="utf-8">
        <body>
         
        </body>
        <script type="text/javascript">
          function htmlFn() {
              console.log('run javascript test')
              return "This value is from index.html"
          }
          </script>
        </html>
        
      2. ets調(diào)用
          Button('runJavaScript')
                .onClick(() => {
                  console.log("run-onclick")
                  //點(diǎn)擊按鈕狭郑,運(yùn)行js方法
                  this.controller.runJavaScript('htmlFn()', (err, val) => {
                    if (err) {
                      console.log('run javascript err ' + JSON.stringify(err))
                      return
                    }
                    if (val) {
                      this.message = val
                    }
                    console.log('run javascript success ' + val);
                  })
                })
                .width('30%')
                .height('30')
        
    2. h5調(diào)用ets方法

      1. ets定義方法

         testObj = {
         
            test:() => {
              this.message = '調(diào)用了當(dāng)前方法'
              console.log('javascript run test 調(diào)用了當(dāng)前方法')
              return 'ArkUI Web Component'
            },
            toString:() => {
              console.log('Web Component toString')
            }
          }
        
      2. 注入到h5

         Button('Register JavaScript To Window')
                .onClick(() => {
                  AlertDialog.show({message:'注冊(cè)方法成功'})
                  try {
                      //注冊(cè)方法到H5的控制器
                    //參數(shù)1:傳入調(diào)用方法的對(duì)象
                    //參數(shù)2:H5在使用該對(duì)象的名字
                    //參數(shù)3:方法列表(數(shù)組)
                    this.controller.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
                  } catch (error) {
                    console.error(`ErrorCode: ${error.code},  Message: ${error.message}`);
                  }
                })
        //registerJavaScriptProxy注冊(cè)的方法,必須刷新才能使用
         Button('refresh')
                .onClick(() => {
                  AlertDialog.show({message:'刷新'})
                  try {
                    this.controller.refresh();
                  } catch (error) {
                    console.error(`ErrorCode: ${error.code},  Message: ${error.message}`);
                  }
                })
        
        //或者不適用registerJavaScriptProxy方法來(lái)注冊(cè)汇在,直接使用Web的方法javaScriptProxy
           Web({ src: $rawfile("second.html"), controller: this.controller })
                // 將對(duì)象注入到web端
                .javaScriptProxy({
                  object: this.testObj,
                  name: "testObjName",
                  methodList: ["test", "toString"],
                  controller: this.controller
                })
        //有什么區(qū)別呢翰萨?目前官網(wǎng)文檔中的例子是,如果ets的方法test返回的是復(fù)雜類型的數(shù)據(jù)糕殉,比如數(shù)組亩鬼,那么使用的是registerJavaScriptProxy
        //但是經(jīng)過測(cè)試殖告,本地模擬器和遠(yuǎn)程模擬器,都無(wú)法傳遞雳锋,可能是模擬器的問題
        
      3. h5調(diào)用

        <!-- index.html -->
        <!DOCTYPE html>
        <html>
        <meta charset="utf-8">
        <body>
        <button style="width:200px;height:200px" type="button" onclick="htmlTest()">Click Me!</button>
        <p id="demo"></p>
        </body>
        <script type="text/javascript">
            function htmlTest() {
              let str=testObjName.test();
              document.getElementById("demo").innerHTML=str;
              console.log('testObj.test result:'+ str)
            }
        </script>
        </html>
        
    3. 參考鏈接: https://blog.csdn.net/Lu_Ca/article/details/135285413或官網(wǎng)https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/web-in-page-app-function-invoking-0000001774279950

    4. 代碼在InterviewQuestion項(xiàng)目中的WebComponent2

注意:

  • 應(yīng)用端通過消息端口(WebMessagePort):發(fā)送消息使用postMessageEvent黄绩,接收消息使用onMessageEvent
  • html端通過消息端口:發(fā)送消息使用postMessage,接收消息監(jiān)聽事件onmessage (都少了event)

問題1: 模擬器中web加載網(wǎng)頁(yè)后玷过,網(wǎng)頁(yè)的按鈕無(wú)法點(diǎn)擊爽丹。
在模擬器中,是無(wú)法測(cè)試html端---應(yīng)用端的數(shù)據(jù)傳遞辛蚊。

  • 本地的模擬器現(xiàn)在不支持webview
  • 可以使用遠(yuǎn)程模擬器粤蝎,或遠(yuǎn)程真機(jī)

問題2: 在將端口0發(fā)送到html端時(shí),使用的方法是postMessage袋马,而真正發(fā)送數(shù)據(jù)是通過postMessageEvent初澎,區(qū)別在哪?

  • postMessage就是用于發(fā)送消息端口的
  • postMessageEvent就是用于發(fā)送消息的
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末飞蛹,一起剝皮案震驚了整個(gè)濱河市谤狡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卧檐,老刑警劉巖墓懂,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異霉囚,居然都是意外死亡捕仔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門盈罐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)榜跌,“玉大人,你說我怎么就攤上這事盅粪〉龊” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵票顾,是天一觀的道長(zhǎng)础浮。 經(jīng)常有香客問我,道長(zhǎng)奠骄,這世上最難降的妖魔是什么豆同? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮含鳞,結(jié)果婚禮上影锈,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好鸭廷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布枣抱。 她就那樣靜靜地躺著,像睡著了一般靴姿。 火紅的嫁衣襯著肌膚如雪沃但。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天佛吓,我揣著相機(jī)與錄音,去河邊找鬼垂攘。 笑死维雇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晒他。 我是一名探鬼主播吱型,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼陨仅!你這毒婦竟也來(lái)了津滞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤灼伤,失蹤者是張志新(化名)和其女友劉穎触徐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狐赡,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撞鹉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颖侄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸟雏。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖览祖,靈堂內(nèi)的尸體忽然破棺而出孝鹊,到底是詐尸還是另有隱情,我是刑警寧澤展蒂,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布又活,位于F島的核電站,受9級(jí)特大地震影響玄货,放射性物質(zhì)發(fā)生泄漏皇钞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一松捉、第九天 我趴在偏房一處隱蔽的房頂上張望夹界。 院中可真熱鬧,春花似錦、人聲如沸可柿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)复斥。三九已至营密,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間目锭,已是汗流浹背评汰。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留痢虹,地道東北人被去。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像奖唯,于是被迫代替她去往敵國(guó)和親惨缆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容