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初始化的屬性
-
初始化PersistentStorage:
PersistentStorage.persistProp('aProp', 47);
-
在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組件。
關(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)度。
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ì),有兩種情況
- 首先公司內(nèi)部將常用的一些效果 , 都封裝了組件
- 比如: listView下拉刷新動(dòng)畫 , 進(jìn)度條等等
- 具體自定義組件如何定義 , 參考上述鏈接
- 其次就是一個(gè)應(yīng)用中 , 如果一個(gè)布局在多個(gè)頁(yè)面中出現(xiàn) , 我們就可以將這個(gè)布局效果封裝為一個(gè)組件
- 比如: 頁(yè)面的頭部標(biāo)題 , 點(diǎn)贊 , 收藏等
- 具體自定義組件如何定義 , 參考上述鏈接
- 首先公司內(nèi)部將常用的一些效果 , 都封裝了組件
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ù)通道
@ohos.web.webview提供web控制能力遵班,web組件提供網(wǎng)頁(yè)顯示的能力
-
webview組件如何使用?
- 添加網(wǎng)絡(luò)權(quán)限: ohos.permission.INTERNET
- 加載網(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 }) } } }
-
ets文件如何與h5通訊?
-
ets調(diào)用h5網(wǎng)頁(yè)方法
- 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>
- 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')
- h5定義方法
-
h5調(diào)用ets方法
-
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') } }
-
注入到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ú)法傳遞雳锋,可能是模擬器的問題
-
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>
-
參考鏈接: 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
代碼在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ā)送消息的