ArkUI與MVVM模式的詩(shī)和遠(yuǎn)方

大家好队塘,我是 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 的基本概念

  1. Model(模型):代表應(yīng)用程序的數(shù)據(jù)結(jié)構(gòu)和業(yè)務(wù)邏輯拜姿。它負(fù)責(zé)存儲(chǔ)數(shù)據(jù)以及可能的數(shù)據(jù)驗(yàn)證烙样、數(shù)據(jù)轉(zhuǎn)換等冯遂。

  2. View(視圖):用戶界面蕊肥,負(fù)責(zé)顯示數(shù)據(jù)(Model)和接收用戶交互。

  3. 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層。

image.png

了解了基本概念后溜歪,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è)案例中肖抱,AddressBookPerson類(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ì)員带兜。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吨灭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刑巧,老刑警劉巖喧兄,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件无畔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吠冤,警方通過(guò)查閱死者的電腦和手機(jī)浑彰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)拯辙,“玉大人郭变,你說(shuō)我怎么就攤上這事⊙谋#” “怎么了诉濒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)夕春。 經(jīng)常有香客問(wèn)我未荒,道長(zhǎng),這世上最難降的妖魔是什么及志? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任片排,我火速辦了婚禮,結(jié)果婚禮上速侈,老公的妹妹穿的比我還像新娘率寡。我一直安慰自己,他們只是感情好倚搬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布勇劣。 她就那樣靜靜地躺著,像睡著了一般潭枣。 火紅的嫁衣襯著肌膚如雪比默。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天盆犁,我揣著相機(jī)與錄音命咐,去河邊找鬼。 笑死谐岁,一個(gè)胖子當(dāng)著我的面吹牛醋奠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伊佃,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼窜司,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了航揉?” 一聲冷哼從身側(cè)響起塞祈,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帅涂,沒(méi)想到半個(gè)月后议薪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體尤蛮,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年斯议,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了产捞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哼御,死狀恐怖坯临,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恋昼,我是刑警寧澤看靠,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站焰雕,受9級(jí)特大地震影響衷笋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矩屁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一辟宗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吝秕,春花似錦泊脐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至约郁,卻和暖如春缩挑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鬓梅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工供置, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绽快。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓芥丧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親坊罢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子续担,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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