大家好队塘,我是 V 哥。今天的內(nèi)容我們來(lái)聊一聊 MVVM 模式在 鴻蒙原生應(yīng)用開(kāi)發(fā)中的使用宜鸯, 比如做過(guò) Android開(kāi)發(fā)的兄弟應(yīng)該清楚憔古,MVVM(Model + View + ViewModel)模式是一種設(shè)計(jì)模式,用于分離應(yīng)用程序的業(yè)務(wù)邏輯淋袖、用戶界面和數(shù)據(jù)模型鸿市。這種模式特別適用于構(gòu)建富客戶端應(yīng)用程序,如桌面或移動(dòng)應(yīng)用即碗。在MVVM模式中焰情,每個(gè)部分都有其特定的職責(zé)。
ArkUI的MVVM工作流程
先來(lái)看一下 MVVM 的基本概念
Model(模型):代表應(yīng)用程序的數(shù)據(jù)結(jié)構(gòu)和業(yè)務(wù)邏輯拜姿。它負(fù)責(zé)存儲(chǔ)數(shù)據(jù)以及可能的數(shù)據(jù)驗(yàn)證烙样、數(shù)據(jù)轉(zhuǎn)換等冯遂。
View(視圖):用戶界面蕊肥,負(fù)責(zé)顯示數(shù)據(jù)(Model)和接收用戶交互。
ViewModel(視圖模型):作為Model和View之間的中介蛤肌,包含從Model中檢索數(shù)據(jù)的邏輯壁却,并提供數(shù)據(jù)給View顯示。它還處理View發(fā)起的命令裸准,如用戶輸入展东,然后更新Model。
在ArkUI中炒俱,MVVM模式的工作流程如下:
- (1)自定義組件通過(guò)執(zhí)行其build()方法或者@Builder裝飾的方法來(lái)渲染UI盐肃,即ViewModel可以渲染View爪膊。
- (2)View可以通過(guò)相應(yīng)的event handler來(lái)改變ViewModel,即事件驅(qū)動(dòng)ViewModel的改變砸王。另外推盛,ViewModel提供了@Watch回調(diào)方法來(lái)監(jiān)聽(tīng)狀態(tài)數(shù)據(jù)的改變。
- (3)在ViewModel被改變時(shí)谦铃,需要同步回Model層耘成,這樣才能保證ViewModel和Model的一致性,即應(yīng)用自身數(shù)據(jù)的一致性驹闰。
- (4)ViewModel結(jié)構(gòu)設(shè)計(jì)應(yīng)始終適配自定義組件的構(gòu)建和更新瘪菌,這也是將Model和ViewModel分開(kāi)的原因。
目前之所以有很多關(guān)于UI構(gòu)造和更新的問(wèn)題嘹朗,都是因?yàn)閂iewModel的設(shè)計(jì)沒(méi)有很好地支持自定義組件的渲染师妙,或者試圖去讓自定義組件強(qiáng)行適配Model層,而中間沒(méi)有用ViewModel來(lái)進(jìn)行分離屹培。例如疆栏,一個(gè)應(yīng)用程序直接將SQL數(shù)據(jù)庫(kù)中的數(shù)據(jù)讀入內(nèi)存,而這種數(shù)據(jù)模型不能很好地直接適配自定義組件的渲染惫谤。因此壁顶,在應(yīng)用程序開(kāi)發(fā)中需要適配ViewModel層。
了解了基本概念后溜歪,V 哥通過(guò)一個(gè)業(yè)務(wù)場(chǎng)景案例來(lái)介紹具體的應(yīng)用若专。
業(yè)務(wù)場(chǎng)景案例:電話簿應(yīng)用
假設(shè)我們要開(kāi)發(fā)一個(gè)簡(jiǎn)單的電話簿應(yīng)用,該應(yīng)用允許用戶查看聯(lián)系人列表蝴猪、編輯和刪除聯(lián)系人信息调衰。
先來(lái)看 Model 層的實(shí)現(xiàn):
1. Model層
首先,我們定義Model層自阱,包括Person
類(lèi)和AddressBook
類(lèi)嚎莉。
// Person.ts
export class Person {
id: number;
name: string;
phone: string;
constructor(id: number, name: string, phone: string) {
this.id = id;
this.name = name;
this.phone = phone;
}
}
// AddressBook.ts
import { Person } from "./Person";
export class AddressBook {
private contacts: Person[] = [];
addContact(person: Person) {
this.contacts.push(person);
}
removeContact(id: number) {
this.contacts = this.contacts.filter(contact => contact.id !== id);
}
getContacts() {
return this.contacts;
}
}
Model 層的實(shí)現(xiàn)比較簡(jiǎn)單,Person
是實(shí)體模型沛豌,表示用戶信息實(shí)體趋箩,AddressBook
是地址溥操作模型,并提供添加加派,刪除和獲取用戶功能叫确。
2. ViewModel層
接下來(lái),我們創(chuàng)建ViewModel層芍锦,它將處理業(yè)務(wù)邏輯并與View層通信竹勉。
// 假設(shè)我們已經(jīng)定義了Person和AddressBook類(lèi)
import { AddressBook } from "./AddressBook";
import { Person } from "./Person";
@ViewModel
class PhoneBookViewModel {
private addressBook: AddressBook;
private selectedContact: Person | null = null;
constructor() {
this.addressBook = new AddressBook();
// 初始添加一些聯(lián)系人
this.addressBook.addContact(new Person(1, "Weige", "1234567890"));
this.addressBook.addContact(new Person(2, "Vin", "0987654321"));
}
getContacts() {
return this.addressBook.getContacts();
}
selectContact(id: number) {
this.selectedContact = this.addressBook.getContacts().find(contact => contact.id === id);
}
deleteContact(id: number) {
this.addressBook.removeContact(id);
this.selectedContact = null;
}
}
PhoneBookView組件使用ForEach循環(huán)來(lái)渲染所有聯(lián)系人,并為每個(gè)聯(lián)系人提供一個(gè)點(diǎn)擊事件娄琉,以便在點(diǎn)擊時(shí)選擇該聯(lián)系人次乓。如果選擇了聯(lián)系人吓歇,它還會(huì)顯示該聯(lián)系人的詳細(xì)信息和一個(gè)刪除按鈕。
3. View層
最后票腰,我們創(chuàng)建View層照瘾,它將顯示數(shù)據(jù)并處理用戶交互。
// 導(dǎo)入必要的模塊和裝飾器
import { PhoneBookViewModel } from "./PhoneBookViewModel";
import { Component, Entry, State } from "@ohos.arkui";
// 使用@Component裝飾器標(biāo)記這個(gè)結(jié)構(gòu)體為一個(gè)組件
@Component
struct PhoneBookView {
// 使用@State裝飾器聲明一個(gè)狀態(tài)變量丧慈,用于存儲(chǔ)選中的聯(lián)系人
@State selectedContact: Person | null = null;
// ViewModel實(shí)例
viewModel: PhoneBookViewModel = new PhoneBookViewModel();
// build函數(shù)用于構(gòu)建UI
build() {
Column() {
// 使用ForEach循環(huán)渲染所有聯(lián)系人
ForEach(this.viewModel.getContacts(), (contact) => {
// 為每個(gè)聯(lián)系人創(chuàng)建一個(gè)行容器
Row() {
// 顯示聯(lián)系人姓名和電話
Text(contact.name).fontSize(20);
Text(contact.phone).fontSize(20);
// 為每個(gè)聯(lián)系人添加點(diǎn)擊事件析命,選擇聯(lián)系人
this.onClick(() => {
this.selectedContact = contact;
});
}
});
// 如果有選中的聯(lián)系人,顯示詳細(xì)信息
if (this.selectedContact) {
Column() {
Text(`Selected Contact: ${this.selectedContact.name}`);
Text(`Phone: ${this.selectedContact.phone}`);
Button("Delete").onClick(() => {
this.viewModel.deleteContact(this.selectedContact.id);
this.selectedContact = null; // 清除選中狀態(tài)
});
}
}
}
}
}
// 使用@Entry裝飾器標(biāo)記這個(gè)組件為頁(yè)面的入口組件
@Entry
struct Main {
build() {
PhoneBookView();
}
}
解釋一下
- PhoneBookView結(jié)構(gòu)體定義了電話簿應(yīng)用的UI逃默。
- 使用ForEach循環(huán)遍歷viewModel中的所有聯(lián)系人鹃愤,并為每個(gè)聯(lián)系人創(chuàng)建一個(gè)包含姓名和電話的Row容器。
- 為每個(gè)聯(lián)系人添加點(diǎn)擊事件完域,當(dāng)點(diǎn)擊時(shí)软吐,將該聯(lián)系人設(shè)置為選中狀態(tài)。
- 如果有選中的聯(lián)系人吟税,顯示其詳細(xì)信息和一個(gè)刪除按鈕凹耙。
- 刪除按鈕的點(diǎn)擊事件會(huì)調(diào)用viewModel的deleteContact方法來(lái)刪除選中的聯(lián)系人,并清除選中狀態(tài)肠仪。
最后小結(jié)一下
在這個(gè)案例中肖抱,AddressBook
和Person
類(lèi)構(gòu)成了Model層,負(fù)責(zé)存儲(chǔ)和處理電話簿數(shù)據(jù)异旧。PhoneBookViewModel
作為ViewModel層意述,處理業(yè)務(wù)邏輯并提供數(shù)據(jù)給View層。PhoneBookView
則是View層吮蛹,負(fù)責(zé)顯示數(shù)據(jù)和接收用戶輸入荤崇。
這種分離確保了代碼的清晰性和可維護(hù)性,同時(shí)也使得單元測(cè)試變得更加容易潮针。ViewModel層的引入术荤,使得View層只需關(guān)注如何展示數(shù)據(jù),而業(yè)務(wù)邏輯則由ViewModel層處理每篷,這樣可以在不改變View層的情況下瓣戚,重用ViewModel層的代碼。關(guān)注威哥愛(ài)編程雳攘,爭(zhēng)做鴻蒙先鋒隊(duì)員带兜。