寫在前面
MVC芬首,MVP,MVVM……移動端的開發(fā)可謂是在MVX的海洋中摸爬滾打逼裆!然而郁稍,V和M的概念不說,關(guān)于P胜宇,關(guān)于VM耀怜,它為什么叫Presenter恢着,為什么叫ViewModel?我們實踐中的P财破,VM所做的事情真的和它們的概念對得上么掰派?
本篇即基于MVVM在移動端的應(yīng)用這一話題做一些簡單的討論,希望大家可以借以回顧自己搭過的框架左痢,碼過的代碼靡羡,能喚起一些有趣更有意義的思考!
第一篇:我對“MVVM”的初識
那是在2015年的時候俊性,MVVM被炒的火熱略步,一日,面朝我已經(jīng)盡心竭力做好了概念分組的“巨型VC”定页,我無力地將頭專向小馬哥趟薄,“編輯這部分代碼我還是感到很難受……”
//某VC中
[aRequester sendPostReqWithUrl:aUrl paras:paraDic response:^(id respData, NSError *error) {
[Error check code];
NSDictionary *tmpDic = (NSDictionary *)respData;
NSNumber *tmpNumber = [tmpDic objectForKey:@"boolVal"] ;
if (YES == [tmpNumber boolValue]) {
_contentView.titleLable.text = [tmpDic objectForKey:@"title"] ;
} else {
_contentView.titleLable.text = [tmpDic objectForKey:@"title2"] ;
}}];
如上,很常見的拯勉,網(wǎng)絡(luò)請求后竟趾,錯誤檢查,解析數(shù)據(jù)并用數(shù)據(jù)更新視圖宫峦。
“如此清晰順暢無比的場景岔帽,還能如何優(yōu)化呢?‘感到難受’导绷,這也沒辦法啦犀勒,什么都不能做了!”我們有這樣自我合理化的內(nèi)心os太正常不過了妥曲,但多年的經(jīng)驗不斷地讓我印證一個真理:事出反常必有鬼(感覺不爽贾费,必可優(yōu)化)。果然檐盟!……
“那是因為你把視圖和邏輯耦合到一起了褂萧!”小馬哥回答。
Step1 可以將我們期望在回調(diào)中做的事情進行簡單的概念分組
//某VC中
[aRequester sendPostReqWithUrl:aUrl paras:paraDic response:^(id respData, NSError *error) {
/* Error check */
/* Handle Parser */
/* Update View */
}];
Step2 分別抽象出解析&更新視圖的具體處理
/* Handle Parser */
- (NSString *)parserTitle:(NSDictionary *respData) {
NSString *tmpStr = nil葵萎;
NSNumber *tmpNumber = [respData objectForKey:@"boolVal"] ;
if (YES == [tmpNumber boolValue]) {
tmpStr = [respData objectForKey:@"title"] ;
} else {
tmpStr = [respData objectForKey:@"title2"] ;
}}];
return tmpStr;
}
/* Update View */
- (void)updateViewWithTitle:(NSString *)title {
_contentView.titleLable.text = title;
}
Step3 方法抽象封裝导犹,并新建文件(logicModel)來承載功能簡單但實現(xiàn)復雜的邏輯代碼
//某VC中
[_logicModel requestTitleInfoWithResponse:^(NSString *title) {
/* Update View */
}];
//封裝到logicModel文件中
- (void)requestUserInfoWithResponse:(void(^)(id userInfo, NSError *error))callback {
[aRequester sendPostReqWithUrl:(NSString *)url paras:(NSDictionary *)paras response:(id respData, NSError *error) {
/* Error check */
/* Handle parser */
/* Callback */
callback(title);
}];
}
有什么不一樣么?或許不經(jīng)過實踐操作中對代碼維護效率精益求精地追求羡忘,很難通過上面的例子直觀的理解抽象的好處谎痢。甚至會有一些浮躁的逆反心理作祟,認為這么做多此一舉卷雕。
抽象/封裝不一定都是好的节猿,它們的應(yīng)用要權(quán)衡地考慮某個實現(xiàn)模塊的復雜性,從而選擇一個最合適的抽象層次漫雕。而抽象/封裝的最基本原則參考滨嘱,我想應(yīng)當是:概念合理峰鄙。
上面的例子即是簡單地將“數(shù)據(jù)的處理”和“視圖的更新”相互獨立起來,使視圖的更新更為純粹九孩!簡言之先馆,我們期望避免下面場景的出現(xiàn)
if (YES == [tmpNumber boolValue]) {
_contentView.titleLable.text = [tmpDic objectForKey:@"title"] ;
} else {
_contentView.titleLable.text = [tmpDic objectForKey:@"title2"] ;
}
然后,小馬哥告訴我躺彬,這就是MVVM,即當前最流行的Model-View-ViewModel模式梅惯。
我上面起名為LogicModel的文件即為小馬哥所說的“VM”宪拥,它負責將網(wǎng)絡(luò)請求、請求解析和一些數(shù)據(jù)處理邏輯進行封裝铣减,從而使VC變得“輕”一些她君。
想象一下我們的思維走向:
1) 與視圖無關(guān)的數(shù)據(jù)邏輯問題,直接往從VM入手排查
2) 與視圖展示有關(guān)的邏輯問題葫哗,在VC到View的流程中一看數(shù)據(jù)對接缔刹,二看VM反饋的數(shù)據(jù)是否有誤。
可謂是結(jié)點清晰劣针,定位問題毫無壓力校镐!大贊!不愧是“MVVM”捺典!
可是鸟廓,VM就是邏輯封裝自然演化的一個“代號”么?
VM = View Model襟己,是視圖的模型引谜,“模型”一詞,從概念上傾向于一種“靜態(tài)”擎浴,而邏輯處理员咽,網(wǎng)絡(luò)請求,信號接收這些趨向于一種異步的“動態(tài)”贮预,而且好像和“視圖”的概念差別有些大贝室。這種封裝固然有它的優(yōu)勢所在,但MVVM的設(shè)計者干嘛對它起名為VM呢萌狂?視圖的模型档玻?叫LogicModel (邏輯模型),或者VCTool(VC工具)怎么都比VM合理吧茫藏?
只是一個名字而已嘛误趴!然而,就我對“大盼癜粒”的理解凉当,他們對于某種概念的名稱擬定枣申,是絕不會馬馬虎虎了事的!
VM的本源必然就是一個VM看杭!一個視圖的模型忠藤!
第二篇:追溯MVVM的提出
或許是我對于VM的理解方向不對吧?畢竟從網(wǎng)上的眾多文章的分析說明楼雹,從同事的實踐中模孩,我們對于MVVM又或MVP的在移動端的應(yīng)用實踐竟然出奇地一致!(如下圖)
翻閱了幾十篇相關(guān)的文章贮缅,每篇的說的頗有道理榨咐,很多文章還是分了上中下篇,并配以圖示谴供,似頗為系統(tǒng)的對MVC块茁,MVP,MVVM進行介紹桂肌。不得不說数焊,這些文章頗具指導意義,確實可以讓很多限于邏輯耦合深淵的朋友找到一盞明燈崎场,讓他們的項目變得清晰而易于維護佩耳。但我還是任性地感覺,他們在打著MVVM的旗號在講VC減負——我想要做的照雁,是接近MVVM的本源蚕愤!
MVVM 最早于 2005 年被微軟的 WPF 和 Silverlight 的架構(gòu)師 John Gossman 提出,并且應(yīng)用在微軟的軟件開發(fā)中饺蚊。我找到了那片博文萍诱,并進行了翻譯和仔細的思考探究。
《Model/View/ViewModel pattern for building WPF apps》
John Gossman
譯文鏈接:
http://www.reibang.com/p/b0b80163782f
原文鏈接: https://blogs.msdn.microsoft.com/johngossman/2005/10/08/introduction-to-modelviewviewmodel-pattern-for-building-wpf-apps/
這篇博文中污呼,有這樣這樣兩句有趣的話:
1)Model/View/ViewModel is a variation of Model/View/Controller (MVC) that is tailored for modern UI development platforms where the View is the responsibility of a designer rather than a classic developer.(譯:MVVM是MVC模式的一個演變裕坊,針對一個視圖的展示樣式秀撇,比起傳統(tǒng)的開發(fā)者愉择,現(xiàn)在往往是設(shè)計師更為關(guān)注,MVVM正是為這種狀況而定制的一種模式黄琼。)
2)The term means "Model of a View", and can be thought of as abstraction of the view苗缩。(譯:它意為“視圖的模型”饵蒂,可以將它想象成一個抽像化的視圖。)
那么酱讶,基于對MVVM本源的解讀退盯,我們在一個“可以被想象成視圖的抽象”的VM中添加大量網(wǎng)絡(luò),頁面跳轉(zhuǎn)等邏輯顯然不太合適了。(它是視圖渊迁,它是視圖慰照,它是視圖,請這樣對自己洗腦琉朽!)同時毒租,我們也想思考下關(guān)于“讓設(shè)計師去完成視圖展示”這個有趣的點。
第三篇:MVVM基于WPF的應(yīng)用(最初的應(yīng)用場景)
WPF即Windows Presentation Foundation箱叁,是微軟推出的基于Windows 的用戶界面框架墅垮。
第二篇的譯文中原作者舉例的應(yīng)用界面是Sparkle,我這邊則以類似的OmniGraffle(一款原型繪制軟件)的界面進行說明蝌蹂。(沒什么特別的原因噩斟,因為我正在用OmniGraffle,更容易截圖:)
如果大家閱讀了第二篇提供的《Model/View/ViewModel pattern for building WPF apps》孤个,你會發(fā)現(xiàn)文中舉例的Sparkle界面操作欄和我舉例的OmniGraffle界面圈紅的部分是很相似的。那么沛简,參照文中的VM劃分方式齐鲤,我們可以設(shè)計A部分對應(yīng)一個ViewModel A(當然OmniGraffle不一定是這樣實現(xiàn)的),B部分對應(yīng)一個ViewModel B椒楣,然后B部分的“填充”给郊,“筆畫”,“陰影”捧灰,“形狀”淆九,“線條”亦可以分別對應(yīng)5個小的ViewModel……(如下圖)
一個有趣的點,View的層次疊加變成了VM的層次疊加毛俏!VM真如一個View的抽象一般炭庙!
同時,第二篇摘錄的另一段譯文引導的另一個問題:什么叫設(shè)計師更關(guān)注UI的頁面展示煌寇?不要小看我們UI同學哦焕蹄,當下很多的設(shè)計師都有css、html的開發(fā)經(jīng)驗阀溶,同時MVVM由微軟提出腻脏,記得微軟有一個自己的XAML吧?它正式一種搭建UI的語言银锻。所以永品,基于MVVM的模式,我們至少可以從概念上將視圖完全剝離(甚至交給UI同學去渲染與實現(xiàn))击纬,模型中只要有視圖中需要展示的元素的具體內(nèi)容數(shù)據(jù)即可鼎姐,他不關(guān)心任何視圖的布局,渲染效果。
同事症见,針對視圖的布局和效果的動態(tài)改變喂走,我們將這些改變抽象成狀態(tài),存放在VM當中谋作。至此芋肠,一個最最簡單的MVVM元組得以實現(xiàn)。
第四篇:MVVM基于APP的應(yīng)用
回到市面上較為流行的一種類“MVVM實踐模式”遵蚜,它們以“MVC+VC減負+概念抽象封裝”作為基本的思路參考帖池,讓VC作為VM和View溝通的主橋梁。
如下圖吭净,一般是一個VC包含一個VM和一個主View睡汹,然后VM或許會處理少量“雙向綁定的任務(wù)”,同時也可能將更多的比如網(wǎng)絡(luò)答復的操作動作回調(diào)給VC去處理視圖更新
這種模式易于理解也確實可以實際的提高代碼的概念性和可維護性寂殉。但我們發(fā)現(xiàn)囚巴,視圖的更新走了兩條長線:
1) 介由VM的綁定實現(xiàn)模型更新視圖;
2) 借由VM的回調(diào)實現(xiàn)VC控制更新視圖友扰。
這總讓我們感到不夠清爽:當我希望將視圖中的一段文字由“我的領(lǐng)導是個壞人”改為“我的領(lǐng)導是個好人”時候彤叉,沒有明確的概念告訴我哪一條“線”是有決策力的“線”(可以成功進行修改的線)。
“看代碼不就知道了村怪?”
請記谆嘟健:
1 有思想的代碼幾乎不需要透過代碼來定位問題
2 維護代價的“積累”不是“疊加”而是“邏輯分支的疊乘”(每一次“選線”的猶豫,都是一層邏輯分支)
所以甚负,看代碼當然可以解決問題!甚至針對復雜的工程梭域,你大可花費一個月將它的每個細節(jié)流程完全理透斑举!然后心滿意足的大贊自我的耐心和代碼閱讀能力!不想碰辅,領(lǐng)導已經(jīng)看到了那句你還沒有來得及改掉的“我的領(lǐng)導是個壞蛋”……
言歸正傳懂昂,第三篇我們基于MVVM在WPF中的應(yīng)用分析貌似還蠻順暢的,但好像應(yīng)用再APP中没宾,有什么地方有些……怪凌彬!根源在哪里?或許如下幾個問題可以作為我們的參考:
1) VC的地位到底更傾向于什么循衰?是V铲敛?是C?是VM会钝?
2) 網(wǎng)絡(luò)請求/視圖生命周期/路由跳轉(zhuǎn)這些在APP端大量出現(xiàn)的概念模塊伐蒋,它們在MVVM中有著怎樣的概念歸屬工三?
4.1 MVVM基于APP的基礎(chǔ)架構(gòu)&模塊分工
我們嘗試一下下面的這套交互結(jié)構(gòu)
首先我們明確一個點,在一個MVC結(jié)構(gòu)中先鱼,即便拋開視圖后俭正,模型和控制器處理的大部分業(yè)務(wù)邏輯,都是為視圖服務(wù)的焙畔。我們常說的“重VC”掸读,很大一部分重在視圖相關(guān)的邏輯或是為之服務(wù)的邏輯。
所以宏多,當我們抽象一些視圖的基礎(chǔ)模型儿惫,并通過VM將視圖本身的(不需要與外界交互的)狀態(tài)變遷邏輯封裝在一個MVVM組的內(nèi)部,對外(對VC)只暴露必要的數(shù)據(jù)更新和消息回調(diào)接口伸但。繁瑣的視圖邏輯就可以被限制在一個MVVM當中(它確實也應(yīng)當在那里)肾请。這時留在VC中的邏輯,一般情況下就很少了更胖。如果此刻的VC還讓你感到“重”的話铛铁,我們大可再對其抽象一個VC-Logic,將復雜的邏輯進行封裝却妨。
各個模塊所負責的主要工作可以參考下圖
如圖避归,VC中的“生命周期控制”,“網(wǎng)絡(luò)請求”管呵,“路由”,View中的“視圖布局”哺窄,“控件效果”都很好理解捐下,讓人一眼摸不清的概念主要存在所謂的VM當中,我們來簡單說明:
1)什么是“處理視圖狀態(tài)”萌业?
視圖可能根據(jù)不同的狀態(tài)有不同的展示內(nèi)容坷襟,甚至展示效果。我們常見的“cur”(current)前綴就適用于說明這種場景生年∮こ蹋“當前選擇的模塊”,“某個按鈕當前的選擇狀態(tài)”抱婉,這些表示視圖狀態(tài)的操作變量的定義應(yīng)當在VM當中档叔,相關(guān)的邏輯交互也應(yīng)當在VM當中。如果說View提供了視圖的所有展示元素蒸绩;那么VM則可以確定某個視圖模塊某一時刻某一個狀態(tài)下的呈現(xiàn)內(nèi)容衙四。
2)什么是“處理視圖協(xié)作”?
一個VM不一定只和一個View存在關(guān)聯(lián)患亿,它可能同時協(xié)調(diào)多個視圖传蹈。
我們以同程旅行的一個篩選界面作為參考場景進行說明:
當我們將“4.5分以上”后面的對號勾上的時候,上面的“4.5分以上”會被同步勾取,同時惦界,“評分”后面會多出個小綠點挑格,這表示評分這頁的篩選條件選擇的不是默認的“不限”。很顯然沾歪,關(guān)鍵詞模塊漂彤、篩選分類模塊、篩選詳情模塊正常人都會分成3部分視圖繪制瞬逊。這三個視圖間顯然是有交互關(guān)系的(即“篩選詳情模塊”的勾選觸發(fā)了“關(guān)鍵詞模塊”的高亮和“篩選分類模塊”的加點)显歧,而VM即是處理這種交互關(guān)系理想場所。
3)什么是“數(shù)據(jù)綁定”确镊?
這邊特指將一個模型數(shù)據(jù)和視圖中的一個展示內(nèi)容進行關(guān)聯(lián)綁定士骤;
單向綁定一般指模型數(shù)據(jù)變化觸發(fā)對應(yīng)的視圖數(shù)據(jù)變化
雙向綁定指模型數(shù)據(jù),視圖數(shù)據(jù)任意一方變化蕾域,都會觸發(fā)另一方的同步變化拷肌。
4)什么是“數(shù)據(jù)轉(zhuǎn)換”?
我們不能企望所有的模型數(shù)據(jù)都能直接被視圖使用旨巷,比如模型中是一個BOOL(0/1)值巨缘,而對應(yīng)的視圖展示期望為“是”/“否”,類似這樣的數(shù)據(jù)轉(zhuǎn)化工作采呐,交給VM吧若锁!
4.2 MVVM基于APP的抽象討論
我們再來討論一下幾個觀點的理解:
1)VC是特殊的VM
很常見的,VC中除了主要的展示視圖外斧吐,還有一個導航條(NavigationBar)又固,而我們又很常見導航條要根據(jù)主視圖的滾動而改變展示效果(比如隨著視圖滾動變得透明),這種視圖的交互顯然只能在VC中處理煤率。這很正常仰冠,VC可以理解為特殊的VM,即它會負責一些類似VM的協(xié)調(diào)工作(協(xié)調(diào)本身也是C的職責)蝶糯,亦會負責VC的其他本職工作(如控制視圖生命周期等)洋只。
2)VM的是可以存在類似View的層次的
寫視圖,Subview(子視圖)的概念是逃不掉的昼捍,而參照“將VM理解為視圖”的思路识虚,復雜視圖中,VM的層次也是逃不掉的端三,像圖中一樣舷礼。
大家會發(fā)現(xiàn),我在VC下面標明了“mainVM”郊闯,在一些MVVM下面標明了“mini”妻献,mainVM好理解蛛株,因為前面我們已經(jīng)引入了“VC是特殊的VM這一思路”,但是mini呢育拨?
大多數(shù)場景谨履,我們一個頁面的視圖交互不會特別復雜,所以熬丧,一般的多層視圖笋粟,只用一個VM管理就夠了。但有時我們會希望對視圖層中的小模塊進行MVVM封裝析蝴,因為它是“通用”的(希望被復用的)害捕,通用的小視圖模塊往往是“簡單”的,這是mini的第一層含義闷畸。
同時尝盼,當我們沒有引入VM概念的時候,View就單純地是View么佑菩?想想UIButton吧盾沫,可以設(shè)定選擇狀態(tài)不說,它還可以隨時獲取當前按鈕的選擇狀態(tài)(selected)殿漠,這不就是說UIButton保存了視圖狀態(tài)么赴精!如果把UIButton進行細致的概念拆分,不就變成了我們的MVVM組么绞幌!所以蕾哟,我們很多系統(tǒng)的視圖控件,本來就可以理解為mini的MVVM莲蜘。
VM從某種角度上講渐苏,就是一個視圖!
第五篇:RAC對iOS實踐MVVM的價值
很顯然菇夸,上面的講述中,我們只字未提到RAC(ReactiveCocoa)仪吧,所以庄新,RAC本身是和MVVM沒有本質(zhì)上的關(guān)聯(lián)的。但無可反駁的是薯鼠,使用RAC確實能讓MVVM的實踐上顯得更加精巧择诈。
5.1 快速綁定
我們在應(yīng)用中運用的視圖更新接口,block回調(diào)出皇,代理羞芍,通知,KVO郊艘,目標動作對……都可以理解為廣義“綁定”所依賴的技巧荷科,RAC則將上述機制統(tǒng)一成“消息”唯咬,可以讓我們以更簡單的方式處理綁定動作。請看下面的例子畏浆。
1)常規(guī)方式:雙向綁定一個字符串和一個textField的text值
/* 1. 使textField中的text改變時胆胰,字符串textStr可以同步變化 */
[_textField addTarget:self action:@selector(valueChanged:) forControlEvents:UIControlEventEditingChanged];
- (void)valueChanged:(UITextField *)textField {
_textStr = _textField.text;
}
/* 2. 使textStr改變時,textField中的text可以同步變化 */
[self addObserver:self
forKeyPath:@"textStr"
options:NSKeyValueObservingOptionNew
context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (object == self && [keyPath isEqualToString:@"textStr"]) {
_textField.text = _textStr;
}
}
2)RAC方式:雙向綁定一個字符串和一個textField的text值
/* 1. 使textField中的text改變時刻获,字符串textStr可以同步變化 */
RAC(self, textStr) = _textField.rac_textSignal;
/* 2. 使textStr改變時蜀涨,textField中的text可以同步變化 */
RAC(self.textField, text) = RACObserve(self, textStr);
代碼的簡化是顯而易見的。
5.2 多元監(jiān)聽
iOS中對于代理的應(yīng)用場景還是很多的蝎毡。而基本的代理模式中厚柳,某個模塊的代理者只能有一個。為了讓多個對象同時接收代理消息沐兵,我們不得不修改模塊結(jié)構(gòu)别垮,又或者自定制一個自以為很簡單完美的代理隊列,又或?qū)⒋?strong>改用通知痒筒?T兹颉(不想玩死自己的話,放棄在局部使用這種思路吧簿透!)甚至移袍,還有更奇葩的設(shè)計。
然而老充,在RAC中很簡單葡盗。
下面的代碼即實現(xiàn)了textStr和textStr2同時監(jiān)聽textField的text的變化
(處理代理一樣的簡單,因為RAC全部將其抽象成為了“消息”)
RAC(self, textStr) = _textField.rac_textSignal;
RAC(self, textStr2) = _textField.rac_textSignal;
但是啡浊,應(yīng)用中的意義呢觅够?
將我們封裝的VM可以理解為一個模塊,對一個模塊而言巷嚣,沒什么比輸入輸出接口的設(shè)計更加重要了喘先。而互聯(lián)網(wǎng)時代,神奇的需求變動在很多場景下讓我們不得不對模塊進行更新廷粒,甚至更新模塊的對外接口窘拯。多元監(jiān)聽的支持可以大大降低模塊對外接口更新的復雜性。(接口的更新很容易牽連整個模塊的基礎(chǔ)框架坝茎,更細節(jié)的分析在此不再贅述)
5.3 元組的引入
元組涤姊,并不是一個讓人感到陌生的概念,它即代表一組約定的有序數(shù)據(jù)嗤放,該組數(shù)據(jù)中每個數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)不需要統(tǒng)一思喊。
在MVVM中(其實普通的視圖設(shè)計中也是),為了方便視圖的展示次酌,我們常常要約定一些輕量級的純視圖數(shù)據(jù)結(jié)構(gòu)恨课。這時候Tuple或許會是最契合我們場景的概念舆乔。
為什么?
1)Tuple的概念定位不同于Array庄呈,tuple的長度一般是確定的蜕煌,tuple組內(nèi)每個元素的類型不要求一致。
2)Tuple的概念亦不同于Dictionary诬留,tuple無須將一個數(shù)組分為key斜纪,value兩個部分(繁瑣,麻煩~)文兑,同時盒刚,tuple是有序的(字典是無序的)。
當然绿贞,介于tuple的靈活性特征因块,使用場景一定要控制在小范圍,需要定義對象的時候籍铁,還是要定義的涡上!萬萬不可將tuple在不可控的大范圍使用。(一樣是玩死自己的行為)拒名。
5.4 沒有RAC不能應(yīng)用MVVM吩愧?
我不這么認為:
1)如我們之前說過的,MVVM與RAC沒有本質(zhì)的關(guān)聯(lián)增显;
2)如5.1~5.3雁佳,RAC可以使我們應(yīng)用MVVM的一些場景變得更為簡單優(yōu)雅,RAC針對MVVM優(yōu)化的問題在我們不使用MVVM時依然也存在(你用MVC是有些場景一樣要用KVO)同云,而且糖权,這些場景不算是決定性的(雙向綁定的實踐應(yīng)用場景其實很少)
結(jié)語
這篇文章的準備在一個月前,中間因為主工作項目原因有各種間斷和擱置炸站,但也慶幸有這樣的項目需求星澳,可以讓我將其中部分的思路得以實踐和印證。相信這篇從MVVM的提出為出發(fā)點旱易,經(jīng)過了反復思考印證募判,將模塊的概念分工多次推倒重組的文章,可以真正為大家對MVVM理解上提供有價值的思路參考咒唆,為實踐中遇到的一些讓人感到不舒服的代碼的優(yōu)化方向上提供有價值的思路參考,為MVVM在移動端的應(yīng)用實踐提供有價值的思路參考释液!