<a >滴滴/淘寶/微信/蘑菇街/casatwy等 iOS App的組件化架構(gòu)漫談</a>
http://www.swifthumb.com/forum.php?mod=viewthread&tid=14714&fromuid=8811
(出處: 敏捷大拇指)
前段時間公司項目打算重構(gòu)膊爪,準(zhǔn)確來說應(yīng)該是按之前的產(chǎn)品邏輯重寫一個項目?
?。在重構(gòu)項目之前涉及到架構(gòu)選型的問題穗酥,我和組里小伙伴一起研究了一下組件化架構(gòu)瞬浓,打算將項目重構(gòu)為組件化架構(gòu)。當(dāng)然不是直接拿來照搬机打,還是要根據(jù)公司具體的業(yè)務(wù)需求設(shè)計架構(gòu)矫户。
在學(xué)習(xí)組件化架構(gòu)的過程中,從很多高質(zhì)量的博客中學(xué)到不少東西残邀,例如蘑菇街李忠皆辽、casatwy、bang的博客芥挣。在學(xué)習(xí)過程中也遇到一些問題驱闷,在微博和QQ上和一些做iOS的朋友進(jìn)行了交流,非常感謝這些朋友的幫助空免。
本篇文章主要針對于之前蘑菇街提出的組件化方案空另,以及casatwy提出的組件化方案進(jìn)行分析,后面還會簡單提到滴滴蹋砚、淘寶扼菠、微信的組件化架構(gòu)摄杂,最后會簡單說一下我公司設(shè)計的組件化架構(gòu)。
1循榆、組件化架構(gòu)的由來
隨著移動互聯(lián)網(wǎng)的不斷發(fā)展析恢,很多程序代碼量和業(yè)務(wù)越來越多,現(xiàn)有架構(gòu)已經(jīng)不適合公司業(yè)務(wù)的發(fā)展速度了秧饮,很多都面臨著重構(gòu)的問題映挂。
在公司項目開發(fā)中,如果項目比較小浦楣,普通的單工程+MVC架構(gòu)就可以滿足大多數(shù)需求了袖肥。但是像淘寶、蘑菇街振劳、微信這樣的大型項目椎组,原有的單工程架構(gòu)就不足以滿足架構(gòu)需求了。
就拿淘寶來說历恐,淘寶在13年開啟的“All in 無線”戰(zhàn)略中寸癌,就將阿里系大多數(shù)業(yè)務(wù)都加入到手機(jī)淘寶中,使客戶端出現(xiàn)了業(yè)務(wù)的爆發(fā)弱贼。在這種情況下蒸苇,單工程架構(gòu)則已經(jīng)遠(yuǎn)遠(yuǎn)不能滿足現(xiàn)有業(yè)務(wù)需求了。所以在這種情況下吮旅,淘寶在13年開啟了插件化架構(gòu)的重構(gòu)溪烤,后來在14年迎來了手機(jī)淘寶有史以來最大規(guī)模的重構(gòu),將其徹底重構(gòu)為組件化架構(gòu)庇勃。
2檬嘀、蘑菇街的組件化架構(gòu)
2.1、原因
在一個項目越來越大责嚷,開發(fā)人員越來越多的情況下鸳兽,項目會遇到很多問題。
業(yè)務(wù)模塊間劃分不清晰罕拂,模塊之間耦合度很大揍异,非常難維護(hù)。
所有模塊代碼都編寫在一個項目中爆班,測試某個模塊或功能衷掷,需要編譯運(yùn)行整個項目。
耦合嚴(yán)重的工程
為了解決上面的問題柿菩,可以考慮加一個中間層來協(xié)調(diào)模塊間的調(diào)用戚嗅,所有的模塊間的調(diào)用都會經(jīng)過中間層中轉(zhuǎn)。(注意看兩張圖的箭頭方向)
添加中間層
但是發(fā)現(xiàn)增加這個中間層后,耦合還是存在的渡处。中間層對被調(diào)用模塊存在耦合,其他模塊也需要耦合中間層才能發(fā)起調(diào)用祟辟。這樣還是存在之前的相互耦合的問題医瘫,而且本質(zhì)上比之前更麻煩了。
2.2旧困、大體結(jié)構(gòu)
所以應(yīng)該做的是醇份,只讓其他模塊對中間層產(chǎn)生耦合關(guān)系,中間層不對其他模塊發(fā)生耦合吼具。
對于這個問題僚纷,可以采用組件化的架構(gòu),將每個模塊作為一個組件拗盒。并且建立一個主項目怖竭,這個主項目負(fù)責(zé)集成所有組件。這樣帶來的好處是很多的:
業(yè)務(wù)劃分更佳清晰陡蝇,新人接手更佳容易痊臭,可以按組件分配開發(fā)任務(wù)。
項目可維護(hù)性更強(qiáng)登夫,提高開發(fā)效率广匙。
更好排查問題,某個組件出現(xiàn)問題恼策,直接對組件進(jìn)行處理鸦致。
開發(fā)測試過程中,可以只編譯自己那部分代碼涣楷,不需要編譯整個項目代碼分唾。
組件化結(jié)構(gòu)
進(jìn)行組件化開發(fā)后,可以把每個組件當(dāng)做一個獨(dú)立的app总棵,每個組件甚至可以采取不同的架構(gòu)鳍寂,例如分別使用MVVM、MVC情龄、MVCS等架構(gòu)迄汛。
2.3、MGJRouter方案
蘑菇街通過MGJRouter實(shí)現(xiàn)中間層骤视,通過MGJRouter進(jìn)行組件間的消息轉(zhuǎn)發(fā)鞍爱,從名字上來說更像是路由器。實(shí)現(xiàn)方式大致是专酗,在提供服務(wù)的組件中提前注冊block睹逃,然后在調(diào)用方組件中通過URL調(diào)用block,下面是調(diào)用方式。
2.3.1沉填、架構(gòu)設(shè)計
MGJRouter組件化架構(gòu)
MGJRouter是一個單例對象疗隶,在其內(nèi)部維護(hù)著一個“URL -> block”格式的注冊表,通過這個注冊表來保存服務(wù)方注冊的block翼闹,以及使調(diào)用方可以通過URL映射出block斑鼻,并通過MGJRouter對服務(wù)方發(fā)起調(diào)用。
在服務(wù)方組件中都對外提供一個接口類猎荠,在接口類內(nèi)部實(shí)現(xiàn)block的注冊工作坚弱,以及block對外提供服務(wù)的代碼實(shí)現(xiàn)。每一個block都對應(yīng)著一個URL关摇,調(diào)用方可以通過URL對block發(fā)起調(diào)用荒叶。
在程序開始運(yùn)行時,需要將所有服務(wù)方的接口類實(shí)例化输虱,以完成這個注冊工作些楣,使MGJRouter中所有服務(wù)方的block可以正常提供服務(wù)。在這個服務(wù)注冊完成后悼瓮,就可以被調(diào)用方調(diào)起并提供服務(wù)戈毒。
蘑菇街項目使用git作為版本控制工具,將每個組件都當(dāng)做一個獨(dú)立工程横堡,并建立主項目來集成所有組件埋市。集成方式是在主項目中通過CocoaPods來集成,將所有組件當(dāng)做二方庫集成到項目中命贴。詳細(xì)的集成技術(shù)點(diǎn)在下面“標(biāo)準(zhǔn)組件化架構(gòu)設(shè)計”章節(jié)中會講到道宅。
2.3.2、MGJRouter調(diào)用
代碼模擬對詳情頁的注冊胸蛛、調(diào)用污茵,在調(diào)用過程中傳遞id參數(shù)。下面是注冊的示例代碼:
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01
02
03
04
[MGJRouter registerURLPattern:@"mgj://detail?id=id"toHandler:^(NSDictionary*routerParameters) {
????// 下面可以在拿到參數(shù)后葬项,為其他組件提供對應(yīng)的服務(wù)
????NSStringuid = routerParameters[@"id"];
}];
通過openURL:方法傳入的URL參數(shù)泞当,對詳情頁已經(jīng)注冊的block方法發(fā)起調(diào)用。調(diào)用方式類似于GET請求民珍,URL地址后面拼接參數(shù)襟士。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01[MGJRouter openURL:@"mgj://detail?id=404"];
也可以通過字典方式傳參,MGJRouter提供了帶有字典參數(shù)的方法嚷量,這樣就可以傳遞非字符串之外的其他類型參數(shù)陋桂。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01[MGJRouter openURL:@"mgj://detail?"withParam:@{@"id": @"404"}];
2.3.3、組件間傳值
有的時候組件間調(diào)用過程中蝶溶,需要服務(wù)方在完成調(diào)用后返回相應(yīng)的參數(shù)嗜历。蘑菇街提供了另外的方法,專門來完成這個操作。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01
02
03
[MGJRouter registerURLPattern:@"mgj://cart/ordercount"toObjectHandler:^id(NSDictionary*routerParamters){
????return@42;
}];
通過下面的方式發(fā)起調(diào)用梨州,并獲取服務(wù)方返回的返回值痕囱,要做的就是傳遞正確的URL和參數(shù)即可。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01NSNumber*orderCount = [MGJRouter objectForURL:@"mgj://cart/ordercount"];
2.3.4暴匠、短鏈管理
這時候會發(fā)現(xiàn)一個問題咐蝇,在蘑菇街組件化架構(gòu)中,存在了很多硬編碼的URL和參數(shù)巷查。在代碼實(shí)現(xiàn)過程中URL編寫出錯會導(dǎo)致調(diào)用失敗,而且參數(shù)是一個字典類型抹腿,調(diào)用方不知道服務(wù)方需要哪些參數(shù)岛请,這些都是個問題。
對于這些數(shù)據(jù)的管理警绩,蘑菇街開發(fā)了一個web頁面崇败,這個web頁面統(tǒng)一來管理所有的URL和參數(shù),Android和iOS都使用這一套URL肩祥,可以保持統(tǒng)一性后室。
2.3.5、基礎(chǔ)組件
在項目中存在很多公共部分的東西混狠,例如封裝的網(wǎng)絡(luò)請求岸霹、緩存、數(shù)據(jù)處理等功能将饺,以及項目中所用到的資源文件贡避。
蘑菇街將這些部分也當(dāng)做組件,劃分為基礎(chǔ)組件予弧,位于業(yè)務(wù)組件下層堪藐。所有業(yè)務(wù)組件都使用同一個基礎(chǔ)組件刑峡,也可以保證公共部分的統(tǒng)一性。
2.4、Protocol方案
2.4.1综看、整體架構(gòu)
Protocol方案的中間件
為了解決MGJRouter方案中URL硬編碼,以及字典參數(shù)類型不明確等問題猪瞬,蘑菇街在原有組件化方案的基礎(chǔ)上推出了Protocol方案膀懈。Protocol方案由兩部分組成,進(jìn)行組件間通信的ModuleManager類以及MGJComponentProtocol協(xié)議類彪置。
通過中間件ModuleManager進(jìn)行消息的調(diào)用轉(zhuǎn)發(fā)拄踪,在ModuleManager內(nèi)部維護(hù)一張映射表,映射表由之前的“URL -> block”變成“Protocol -> Class”拳魁。
在中間件中創(chuàng)建MGJComponentProtocol文件惶桐,服務(wù)方組件將可以用來調(diào)用的方法都定義在Protocol中,將所有服務(wù)方的Protocol都分別定義到MGJComponentProtocol文件中,如果協(xié)議比較多也可以分開幾個文件定義姚糊。這樣所有調(diào)用方依然是只依賴中間件贿衍,不需要依賴除中間件之外的其他組件。
Protocol方案中每個組件也需要一個“接口類”救恨,此類負(fù)責(zé)實(shí)現(xiàn)當(dāng)前組件對應(yīng)的協(xié)議方法贸辈,也就是對外提供服務(wù)的實(shí)現(xiàn)。在程序開始運(yùn)行時將自身的Class注冊到ModuleManager中肠槽,并將Protocol反射出字符串當(dāng)做key擎淤。這個注冊過程和MGJRouter是類似的,都需要提前注冊服務(wù)秸仙。
2.4.2嘴拢、示例代碼
創(chuàng)建MGJUserImpl類當(dāng)做User模塊的服務(wù)類,并在MGJComponentProtocol.h中定義MGJUserProtocol協(xié)議寂纪,由MGJUserImpl類實(shí)現(xiàn)協(xié)議中定義的方法席吴,完成對外提供服務(wù)的過程。下面是協(xié)議定義:
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01
02
03
@protocolMGJUserProtocol <NSObject>
- (NSString*)getUserName;
@end
Class遵守協(xié)議并實(shí)現(xiàn)定義的方法捞蛋,外界通過Protocol獲取的Class實(shí)例化為對象孝冒,調(diào)用服務(wù)方實(shí)現(xiàn)的協(xié)議方法拟杉。
ModuleManager的協(xié)議注冊方法庄涡,注冊時將Protocol反射為字符串當(dāng)做存儲的key,將實(shí)現(xiàn)協(xié)議的Class當(dāng)做值存儲搬设。通過Protocol取Class的時候啼染,就是通過Protocol從ModuleManager中將Class映射出來。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01[ModuleManager registerClass:MGJUserImpl forProtocol:@protocol(MGJUserProtocol)];
調(diào)用時通過Protocol從ModuleManager中映射出注冊的Class焕梅,將獲取到的Class實(shí)例化迹鹅,并調(diào)用Class實(shí)現(xiàn)的協(xié)議方法完成服務(wù)調(diào)用。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01
02
03
Class cls = [[ModuleManager sharedInstance] classForProtocol:@protocol(MGJUserProtocol)];
iduserComponent = [[cls alloc] init];
NSString*userName = [userComponent getUserName];
2.5贞言、整體調(diào)用流程
蘑菇街是OpenURL和Protocol混用的方式斜棚,兩種實(shí)現(xiàn)的調(diào)用方式不同,但大體調(diào)用邏輯和實(shí)現(xiàn)思路類似该窗,所以下面的調(diào)用流程二者差不多弟蚀。在OpenURL不能滿足需求或調(diào)用不方便時,就可以通過Protocol的方式調(diào)用酗失。
在進(jìn)入程序后义钉,先使用MGJRouter對服務(wù)方組件進(jìn)行注冊。每個URL對應(yīng)一個block的實(shí)現(xiàn)规肴,block中的代碼就是服務(wù)方對外提供的服務(wù)捶闸,調(diào)用方可以通過URL調(diào)用這個服務(wù)夜畴。
調(diào)用方通過MGJRouter調(diào)用openURL:方法,并將被調(diào)用代碼對應(yīng)的URL傳入删壮,MGJRouter會根據(jù)URL查找對應(yīng)的block實(shí)現(xiàn)贪绘,從而調(diào)用服務(wù)方組件的代碼進(jìn)行通信。
調(diào)用和注冊block時央碟,block有一個字典用來傳遞參數(shù)税灌。這樣的優(yōu)勢就是參數(shù)類型和數(shù)量理論上是不受限制的,但是需要很多硬編碼的key名在項目中亿虽。
2.6菱涤、內(nèi)存管理
蘑菇街組件化方案有兩種,Protocol和MGJRouter的方式洛勉,但都需要進(jìn)行register操作狸窘。Protocol注冊的是Class,MGJRouter注冊的是Block坯认,注冊表是一個NSMutableDictionary類型的字典,而字典的擁有者又是一個單例對象氓涣,這樣會造成內(nèi)存的常駐牛哺。
下面是對兩種實(shí)現(xiàn)方式內(nèi)存消耗的分析:
首先說一下block實(shí)現(xiàn)方式可能導(dǎo)致的內(nèi)存問題,block如果使用不當(dāng)劳吠,很容易造成循環(huán)引用的問題引润。
經(jīng)過暴力測試,證明并不會導(dǎo)致內(nèi)存問題痒玩。被保存在字典中是一個block對象淳附,而block對象本身并不會占用多少內(nèi)存。在調(diào)用block后會對block體中的方法進(jìn)行執(zhí)行蠢古,執(zhí)行完成后block體中的對象釋放奴曙。
而block自身的實(shí)現(xiàn)只是一個結(jié)構(gòu)體,也就相當(dāng)于字典中存放的是很多結(jié)構(gòu)體草讶,所以內(nèi)存的占用并不是很大洽糟。
對于協(xié)議這種實(shí)現(xiàn)方式,和block內(nèi)存常駐方式差不多堕战。只是將存儲的block對象換成Class對象坤溃,如果不是已經(jīng)實(shí)例化的對象,內(nèi)存占用還是比較小的嘱丢。
3薪介、casatwy組件化方案
3.1、整體架構(gòu)
casatwy組件化方案分為兩種調(diào)用方式越驻,遠(yuǎn)程調(diào)用和本地調(diào)用汁政,對于兩個不同的調(diào)用方式分別對應(yīng)兩個接口道偷。
遠(yuǎn)程調(diào)用通過AppDelegate代理方法傳遞到當(dāng)前應(yīng)用后,調(diào)用遠(yuǎn)程接口并在內(nèi)部做一些處理烂完,處理完成后會在遠(yuǎn)程接口內(nèi)部調(diào)用本地接口试疙,以實(shí)現(xiàn)本地調(diào)用為遠(yuǎn)程調(diào)用服務(wù)。
本地調(diào)用由performTarget: action: params:方法負(fù)責(zé)抠蚣,但調(diào)用方一般不直接調(diào)用performTarget:方法祝旷。CTMediator會對外提供明確參數(shù)和方法名的方法,在方法內(nèi)部調(diào)用performTarget:方法和參數(shù)的轉(zhuǎn)換嘶窄。
casatwy提出的組件化架構(gòu)
3.2怀跛、架構(gòu)設(shè)計思路
casatwy是通過CTMediator類實(shí)現(xiàn)組件化的,在此類中對外提供明確參數(shù)類型的接口柄冲,接口內(nèi)部通過performTarget方法調(diào)用服務(wù)方組件的Target吻谋、Action。由于CTMediator類的調(diào)用是通過runtime主動發(fā)現(xiàn)服務(wù)的现横,所以服務(wù)方對此類是完全解耦的漓拾。
但如果CTMediator類對外提供的方法都放在此類中,將會對CTMediator造成極大的負(fù)擔(dān)和代碼量戒祠。解決方法就是對每個服務(wù)方組件創(chuàng)建一個CTMediator的Category骇两,并將對服務(wù)方的performTarget調(diào)用放在對應(yīng)的Category中,這些Category都屬于CTMediator中間件姜盈,從而實(shí)現(xiàn)了感官上的接口分離低千。
casatwy組件化實(shí)現(xiàn)細(xì)節(jié)
對于服務(wù)方的組件來說,每個組件都提供一個或多個Target類馏颂,在Target類中聲明Action方法示血。Target類是當(dāng)前組件對外提供的一個“服務(wù)類”,Target將當(dāng)前組件中所有的服務(wù)都定義在里面救拉,CTMediator通過runtime主動發(fā)現(xiàn)服務(wù)难审。
在Target中的所有Action方法,都只有一個字典參數(shù)亿絮,所以可以傳遞的參數(shù)很靈活剔宪,這也是casatwy提出的去Model化的概念。在Action的方法實(shí)現(xiàn)中壹无,對傳進(jìn)來的字典參數(shù)進(jìn)行解析葱绒,再調(diào)用組件內(nèi)部的類和方法。
3.3斗锭、架構(gòu)分析
casatwy為我們提供了一個Demo地淀,通過這個Demo可以很好的理解casatwy的設(shè)計思路,下面按照我的理解講解一下這個Demo岖是。
文件目錄
打開Demo后可以看到文件目錄非常清楚帮毁,在上圖中用藍(lán)框框出來的就是中間件部分实苞,紅框框出來的就是業(yè)務(wù)組件部分。我對每個文件夾做了一個簡單的注釋烈疚,包含了其在架構(gòu)中的職責(zé)黔牵。
在CTMediator中定義遠(yuǎn)程調(diào)用和本地調(diào)用的兩個方法,其他業(yè)務(wù)相關(guān)的調(diào)用由Category完成爷肝。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01
02
03
04
// 遠(yuǎn)程App調(diào)用入口
- (id)performActionWithUrl:(NSURL*)url completion:(void(^)(NSDictionary*info))completion;
// 本地組件調(diào)用入口
- (id)performTarget:(NSString*)targetName action:(NSString*)actionName params:(NSDictionary*)params;
在CTMediator中定義的ModuleA的Category猾浦,對外提供了一個獲取控制器并跳轉(zhuǎn)的功能,下面是代碼實(shí)現(xiàn)灯抛。由于casatwy的方案中使用performTarget的方式進(jìn)行調(diào)用金赦,所以涉及到很多硬編碼字符串的問題,casatwy采取定義常量字符串來解決這個問題对嚼,這樣管理也更方便夹抗。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
#import "CTMediator+CTMediatorModuleAActions.h"
NSString* constkCTMediatorTargetA = @"A";
NSString* constkCTMediatorActionNativFetchDetailViewController = @"nativeFetchDetailViewController";
@implementationCTMediator (CTMediatorModuleAActions)
- (UIViewController *)CTMediator_viewControllerForDetail {
????UIViewController *viewController = [selfperformTarget:kCTMediatorTargetA
????????????????????????????????????????????????????action:kCTMediatorActionNativFetchDetailViewController
????????????????????????????????????????????????????params:@{@"key":@"value"}];
????if([viewController isKindOfClass:[UIViewController class]]) {
????????// view controller 交付出去之后,可以由外界選擇是push還是present
????????returnviewController;
????} else{
????????// 這里處理異常場景纵竖,具體如何處理取決于產(chǎn)品
????????return[[UIViewController alloc] init];
????}
}
下面是ModuleA組件中提供的服務(wù)漠烧,被定義在Target_A類中,這些服務(wù)可以被CTMediator通過runtime的方式調(diào)用靡砌,這個過程就叫做發(fā)現(xiàn)服務(wù)已脓。
我們發(fā)現(xiàn),在這個方法中其實(shí)做了參數(shù)處理和內(nèi)部調(diào)用的功能乏奥,這樣就可以保證組件內(nèi)部的業(yè)務(wù)不受外部影響,對內(nèi)部業(yè)務(wù)沒有侵入性亥曹。
[Objective-C]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01
02
03
04
05
06
- (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary*)params {
????// 對傳過來的字典參數(shù)進(jìn)行解析邓了,并調(diào)用ModuleA內(nèi)部的代碼
????DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
????viewController.valueLabel.text = params[@"key"];
????returnviewController;
}
3.4、命名規(guī)范
在大型項目中代碼量比較大媳瞪,需要避免命名沖突的問題骗炉。對于這個問題casatwy采取的是加前綴的方式,從casatwy的Demo中也可以看出蛇受,其組件ModuleA的Target命名為Target_A句葵,被調(diào)用的Action命名為Action_nativeFetchDetailViewController:。
casatwy將類和方法的命名兢仰,都統(tǒng)一按照其功能做區(qū)分當(dāng)做前綴乍丈,這樣很好的將組件相關(guān)和組件內(nèi)部代碼進(jìn)行了劃分。
4把将、標(biāo)準(zhǔn)組件化架構(gòu)設(shè)計
這個章節(jié)叫做“標(biāo)準(zhǔn)組件化架構(gòu)設(shè)計”轻专,對于項目架構(gòu)來說并沒有絕對意義的標(biāo)準(zhǔn)之說。這里說到的“標(biāo)準(zhǔn)組件化架構(gòu)設(shè)計”只是因為采取這樣的方式的人比較多察蹲,且這種方式相比而言較合理请垛。
在上面文章中提到了casatwy方案的CTMediator催训,蘑菇街方案的MGJRouter和ModuleManager,下面統(tǒng)稱為中間件宗收。
4.1漫拭、整體架構(gòu)
組件化架構(gòu)中,首先有一個主工程混稽,主工程負(fù)責(zé)集成所有組件采驻。每個組件都是一個單獨(dú)的工程,創(chuàng)建不同的git私有倉庫來管理荚坞,每個組件都有對應(yīng)的開發(fā)人員負(fù)責(zé)開發(fā)挑宠。開發(fā)人員只需要關(guān)注與其相關(guān)組件的代碼,其他業(yè)務(wù)代碼和其無關(guān)颓影,來新人也好上手各淀。
組件的劃分需要注意組件粒度,粒度根據(jù)業(yè)務(wù)可大可小诡挂。組件劃分后屬于業(yè)務(wù)組件碎浇,對于一些多個組件共同的東西,例如網(wǎng)絡(luò)璃俗、數(shù)據(jù)庫之類的奴璃,應(yīng)該劃分到單獨(dú)的組件或基礎(chǔ)組件中。對于圖片或配置表這樣的資源文件城豁,應(yīng)該再單獨(dú)劃分一個資源組件苟穆,這樣避免資源的重復(fù)性。
服務(wù)方組件對外提供服務(wù)唱星,由中間件調(diào)用或發(fā)現(xiàn)服務(wù)雳旅,服務(wù)對當(dāng)前組件無侵入性,只負(fù)責(zé)對傳遞過來的數(shù)據(jù)進(jìn)行解析和組件內(nèi)調(diào)用的功能间聊。需要被其他組件調(diào)用的組件都是服務(wù)方攒盈,服務(wù)方也可以調(diào)用其他組件的服務(wù)。
通過這樣的組件劃分哎榴,組件的開發(fā)進(jìn)度不會受其他業(yè)務(wù)的影響型豁,可以多個組件單獨(dú)的并行開發(fā)。組件間的通信都交給中間件來進(jìn)行尚蝌,需要通信的類只需要接觸中間件迎变,而中間件不需要耦合其他組件,這就實(shí)現(xiàn)了組件間的解耦飘言。中間件負(fù)責(zé)處理所有組件之間的調(diào)度氏豌,在所有組件之間起到控制核心的作用。
這套框架清晰的劃分了不同組件热凹,從整體架構(gòu)上來約束開發(fā)人員進(jìn)行組件化開發(fā)泵喘,避免某個開發(fā)人員偷懶直接引用頭文件泪电,產(chǎn)生組件間的耦合,破壞整體架構(gòu)纪铺。假設(shè)以后某個業(yè)務(wù)發(fā)生大的改變相速,需要對相關(guān)代碼進(jìn)行重構(gòu),可以在單個組件進(jìn)行重構(gòu)鲜锚。組件化架構(gòu)降低了重構(gòu)的風(fēng)險突诬,保證了代碼的健壯性。
4.2芜繁、組件集成
組件化架構(gòu)圖
每個組件都是一個單獨(dú)的工程旺隙,在組件開發(fā)完成后上傳到git倉庫。主工程通過Cocoapods集成各個組件骏令,集成和更新組件時只需要pod update即可蔬捷。這樣就是把每個組件當(dāng)做第三方來管理,管理起來非常方便榔袋。
Cocoapods可以控制每個組件的版本周拐,例如在主項目中回滾某個組件到特定版本,就可以通過修改podfile文件實(shí)現(xiàn)凰兑。選擇Cocoapods主要因為其本身功能很強(qiáng)大妥粟,可以很方便的集成整個項目,也有利于代碼的復(fù)用吏够。通過這種集成方式勾给,可以很好的避免在傳統(tǒng)項目中代碼沖突的問題。
4.3锅知、集成方式
對于組件化架構(gòu)的集成方式播急,我在看完bang的博客后專門請教了一下bang。根據(jù)在微博上和bang的聊天以及其他博客中的學(xué)習(xí)喉镰,在主項目中集成組件主要分為兩種方式——源碼和framework旅择,但都是通過CocoaPods來集成惭笑。
無論是用CocoaPods管理源碼侣姆,還是直接管理framework,效果都是一樣的沉噩,都是可以直接進(jìn)行pod update之類的操作的捺宗。
這兩種組件集成方案,實(shí)踐中也是各有利弊川蒙。直接在主工程中集成代碼文件蚜厉,可以在主工程中進(jìn)行調(diào)試。集成framework的方式畜眨,可以加快編譯速度昼牛,而且對每個組件的代碼有很好的保密性术瓮。如果公司對代碼安全比較看重,可以考慮framework的形式贰健,但framework不利于主工程中的調(diào)試胞四。
例如手機(jī)QQ或者支付寶這樣的大型程序,一般都會采取framework的形式伶椿。而且一般這樣的大公司辜伟,都會有自己的組件庫,這個組件庫往往可以代表一個大的功能或業(yè)務(wù)組件脊另,直接添加項目中就可以使用导狡。關(guān)于組件化庫在后面講淘寶組件化架構(gòu)的時候會提到。
4.3.1偎痛、不推薦的集成方式
之前有些項目是直接用workspace的方式集成的旱捧,或者直接在原有項目中建立子項目,直接做文件引用看彼。但這兩點(diǎn)都是不建議做的廊佩,因為沒有真正意義上實(shí)現(xiàn)業(yè)務(wù)組件的剝離,只是像之前的項目一樣從文件目錄結(jié)構(gòu)上進(jìn)行了劃分靖榕。
5标锄、組件化開發(fā)總結(jié)
對于項目架構(gòu)來說,一定要建立于業(yè)務(wù)之上來設(shè)計架構(gòu)茁计。不同的項目業(yè)務(wù)不同料皇,組件化方案的設(shè)計也會不同,應(yīng)該設(shè)計最適合公司業(yè)務(wù)的架構(gòu)星压。
5.1践剂、架構(gòu)對比
在除蘑菇街Protocol方案外,其他兩種方案都或多或少的存在硬編碼問題娜膘,硬編碼如果量比較大的話挺麻煩的逊脯。
在casatwy的CTMediator方案中需要硬編碼Target、Action字符串竣贪,只不過這個缺陷被封閉在中間件里面了军洼,將這些字符串都統(tǒng)一定義為常量,外界使用不需要接觸到硬編碼演怎。蘑菇街的MGJRouter的方案也是一樣的匕争,也有硬編碼URL的問題,蘑菇街可能也做了類似的處理爷耀。
casatwy和蘑菇街提出的兩套組件化方案甘桑,大體結(jié)構(gòu)是類似的,三套方案都分為調(diào)用方、中間件跑杭、服務(wù)方铆帽,只是在具體實(shí)現(xiàn)過程中有些不同。例如Protocol方案在中間件中加入了Protocol文件德谅,casatwy的方案在中間件中加入了Category锄贼。
三種方案內(nèi)部都有容錯處理,所以三種方案的穩(wěn)定性都是比較好的女阀,而且都可以拿出來單獨(dú)運(yùn)行宅荤,在服務(wù)方不存在的情況下也不會有問題。
在三套方案中浸策,服務(wù)方都對外提供一個供外界調(diào)用的接口類冯键,這個類中實(shí)現(xiàn)組件對外提供的服務(wù),中間件通過接口類來實(shí)現(xiàn)組件間的通信庸汗。在此類中統(tǒng)一定義對外提供的服務(wù)惫确,外界調(diào)用時就知道服務(wù)方可以做什么。
調(diào)用流程也不大一樣蚯舱,蘑菇街的兩套方案都需要注冊操作改化,無論是Block還是Protocol都需要注冊后才可以提供服務(wù)。而casatwy的方案則不需要枉昏,直接通過runtime調(diào)用陈肛。casatwy的方案實(shí)現(xiàn)了真正的對服務(wù)方解耦,而蘑菇街的兩套方案則沒有兄裂,對服務(wù)方和調(diào)用方都造成了耦合句旱。
我認(rèn)為三套方案中,Protocol方案是調(diào)用和維護(hù)最麻煩的一套方案晰奖。維護(hù)時需要同時維護(hù)Protocol谈撒、接口類兩部分。而且調(diào)用時需要將服務(wù)方的接口類返回給調(diào)用方匾南,并由調(diào)用方執(zhí)行一系列調(diào)用邏輯啃匿,調(diào)用一個服務(wù)的邏輯非常復(fù)雜,這在開發(fā)中是非常影響開發(fā)效率的蛆楞。
5.2溯乒、總結(jié)
下面是組件化開發(fā)中的一個小總結(jié),也是開發(fā)過程中的一些注意點(diǎn)臊岸。
5.2.1橙数、在MGJRouter方案中尊流,是通過調(diào)用OpenURL:方法并傳入URL來發(fā)起調(diào)用帅戒。鑒于URL協(xié)議名等固定格式,可以通過判斷協(xié)議名的方式,使用配置表控制H5和native的切換逻住,配置表可以從后臺更新钟哥,只需要將協(xié)議名更改一下即可。
[Swift]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01
02
mgj://detail?id=123456
[url=http://www.mogujie.com/detail?id=123456]http://www.mogujie.com/detail?id=123456[/url]
假設(shè)現(xiàn)在線上的native組件出現(xiàn)嚴(yán)重bug瞎访,在后臺將配置文件中原有的本地URL換成H5的URL腻贰,并更新客戶端配置文件。在調(diào)用MGJRouter時傳入這個H5的URL即可完成切換扒秸,MGJRouter判斷如果傳進(jìn)來的是一個H5的URL就直接跳轉(zhuǎn)webView播演。而且URL可以傳遞參數(shù)給MGJRouter,只需要MGJRouter內(nèi)部做參數(shù)截取即可伴奥。
5.2.2写烤、casatwy方案和蘑菇街Protocol方案,都提供了傳遞明確類型參數(shù)的方法拾徙。在MGJRouter方案中洲炊,傳遞參數(shù)主要是通過類似GET請求一樣在URL后面拼接參數(shù),和在字典中傳遞參數(shù)兩種方式組成尼啡。這兩種方式會造成傳遞參數(shù)類型不明確暂衡,傳遞參數(shù)類型受限(GET請求不能傳遞對象)等問題,后來使用Protocol方案彌補(bǔ)這個問題崖瞭。
5.2.3狂巢、組件化開發(fā)可以很好的提升代碼復(fù)用性插龄,組件可以直接拿到其他項目中使用撑刺,這個優(yōu)點(diǎn)在下面淘寶架構(gòu)中會著重講一下。
5.2.4述呐、對于調(diào)試工作寺惫,應(yīng)該放在每個組件中完成疹吃。單獨(dú)的業(yè)務(wù)組件可以直接提交給測試提測,這樣測試起來也比較方便西雀。最后組件開發(fā)完成并測試通過后萨驶,再將所有組件更新到主項目,提交給測試進(jìn)行集成測試即可艇肴。
5.2.5腔呜、使用組件化架構(gòu)開發(fā),組件間的通信都是有成本的再悼。所以盡量將業(yè)務(wù)封裝在組件內(nèi)部核畴,對外只提供簡單的接口。即“高內(nèi)聚冲九、低耦合”原則谤草。
5.2.6、把握好劃分粒度的細(xì)化程度,太細(xì)則項目過于分散丑孩,太大則項目組件臃腫冀宴。但是項目都是從小到大的一個發(fā)展過程,所以不斷進(jìn)行重構(gòu)是掌握這個組件的細(xì)化程度最好的方式温学。
6略贮、我公司架構(gòu)
下面就簡單說說我公司項目架構(gòu),公司項目是一個地圖導(dǎo)航應(yīng)用仗岖,業(yè)務(wù)層之下的基礎(chǔ)組件占比較大逃延。且基礎(chǔ)組件相對比較獨(dú)立,對外提供了很多調(diào)用接口轧拄。剛開始想的是采用MGJRouter的方案真友,但如果這些調(diào)用都通過Router進(jìn)行,開發(fā)起來比較復(fù)雜紧帕,反而會適得其反盔然。最主要我們項目也并不是非常大,沒必要都用Router轉(zhuǎn)發(fā)是嗜。
對于這個問題愈案,公司項目的架構(gòu)設(shè)計是:層級架構(gòu)+組件化架構(gòu),組件化架構(gòu)處于層級架構(gòu)的最上層鹅搪,也就是業(yè)務(wù)層站绪。采取這種結(jié)構(gòu)混合的方式進(jìn)行整體架構(gòu),這個對于公共組件的管理和層級劃分比較有利丽柿,符合公司業(yè)務(wù)需求恢准。
公司組件化架構(gòu)
對于業(yè)務(wù)層級依然采用組件化架構(gòu)的設(shè)計,這樣可以充分利用組件化架構(gòu)的優(yōu)勢甫题,對項目組件間進(jìn)行解耦馁筐。在上層和下層的調(diào)用中,下層的功能組件應(yīng)該對外開放一個接口類坠非,在接口類中聲明所有的服務(wù)敏沉,實(shí)現(xiàn)上層調(diào)用當(dāng)前組件的一個中轉(zhuǎn),上層直接調(diào)用接口類炎码。這樣做的好處在于盟迟,如果下層發(fā)生改變不會對上層造成影響,而且也省去了部分Router轉(zhuǎn)發(fā)的工作潦闲。
在設(shè)計層級架構(gòu)時攒菠,需要注意只能上層對下層依賴,下層對上層不能有依賴歉闰,下層中不要包含上層業(yè)務(wù)邏輯辖众。對于項目中存在的公共資源和代碼卓起,應(yīng)該將其下沉到下層中。
6.1赵辕、為什么這么做?
首先就像我剛才說的概龄,我公司項目并不是很大还惠,根本沒必要拆分的那么徹底。
因為組件化開發(fā)有一個很重要的原因就是解耦合私杜,如果我做到了底層不對上層依賴蚕键,這樣就已經(jīng)解除了上下層的相互耦合。而且上層對下層進(jìn)行調(diào)用的時候衰粹,也不是直接調(diào)用下層锣光,通過一個接口類進(jìn)行中轉(zhuǎn),實(shí)現(xiàn)了下層的改變對上層無影響铝耻,這也是上層對下層解耦的表現(xiàn)誊爹。
所以對于第三方就不用說了,上層直接調(diào)用下層的第三方也是沒問題的瓢捉,這都是解耦的频丘。
6.2、模型類怎么辦泡态,放在哪合適搂漠?
casatwy對模型類的觀點(diǎn)是去Model化,簡單來說就是用字典代替Model存儲數(shù)據(jù)某弦。這對于組件化架構(gòu)來說桐汤,是解決組件之間數(shù)據(jù)傳遞的一個很好的方法。
因為模型類是關(guān)乎業(yè)務(wù)的靶壮,理論上必須放在業(yè)務(wù)層也就是業(yè)務(wù)組件這一層怔毛。但是要把模型對象從一個組件中當(dāng)做參數(shù)傳遞到另一個組件中,模型類放在調(diào)用方和服務(wù)方的哪個組件都不太合適腾降,而且有可能不只兩個組件使用到這個模型對象馆截。這樣的話在其他組件使用模型對象,必然會造成引用和耦合蜂莉。
那么如果把模型類放在Router中蜡娶,這樣會造成Router耦合了業(yè)務(wù),造成業(yè)務(wù)的侵入性映穗。如果在用到這個模型對象的所有組件中窖张,都分別維護(hù)一份相同的模型類,這樣之后業(yè)務(wù)發(fā)生改變模型類就會很麻煩蚁滋。
6.2.1宿接、那應(yīng)該怎么辦呢赘淮?
如果將模型類單獨(dú)拉出來,定義一個模型組件呢睦霎?這個看起來比較可行梢卸,將這個定義模型的組件下沉到下層,模型組件不包含業(yè)務(wù)副女,只聲明模型對象的類蛤高。但是一般組件的模型對象都是當(dāng)前組件內(nèi)使用的,將模型對象傳遞給其他組件的需求非常少碑幅,那所有的模型類都定義到模型組件嗎戴陡?
對于這個問題,我建議在項目開發(fā)中將模型類還定義在當(dāng)前業(yè)務(wù)組件中沟涨,在組件間傳遞模型對象時進(jìn)行去Model化恤批,傳遞字典類型的參數(shù)。
上面只是思考裹赴,恰巧我公司持久化方案用的是CoreData喜庞,所有模型的定義都在CoreData組件中,這樣就避免了業(yè)務(wù)層組件之間因為模型類的耦合棋返。
7赋荆、滴滴組件化架構(gòu)
之前看過滴滴iOS負(fù)責(zé)人李賢輝的技術(shù)分享,詳見iOS開發(fā)社區(qū)敏捷大拇指(Swifthumb.com)帖子《滴滴打車 iOS App的組件化實(shí)踐與優(yōu)化》懊昨,分享的是滴滴iOS客戶端的架構(gòu)發(fā)展歷程窄潭,下面簡單總結(jié)一下。
7.1酵颁、發(fā)展歷程
滴滴在最開始的時候架構(gòu)較混亂嫉你。然后在2.0時期重構(gòu)為MVC架構(gòu),使項目劃分更加清晰躏惋。在3.0時期上線了新的業(yè)務(wù)線幽污,這時采用的游戲開發(fā)中的狀態(tài)機(jī)機(jī)制,暫時可以滿足現(xiàn)有業(yè)務(wù)簿姨。
然而在后期不斷上線順風(fēng)車距误、代駕、巴士等多條業(yè)務(wù)線的情況下扁位,現(xiàn)有架構(gòu)變得非常臃腫准潭,代碼耦合嚴(yán)重。從而在2015年開始了代號為“The One”的方案域仇,這套方案就是滴滴的組件化方案刑然。
7.2、架構(gòu)設(shè)計
滴滴的組件化方案暇务,和蘑菇街方案類似泼掠,也是通過私有CocoaPods來管理各個組件怔软。將整個項目拆分為業(yè)務(wù)部分和技術(shù)部分,業(yè)務(wù)部分包括專車择镇、拼車挡逼、巴士等業(yè)務(wù)模塊,每個業(yè)務(wù)模塊就是一個單獨(dú)的組件腻豌,使用一個pods管理家坎。技術(shù)部分則分為登錄分享、網(wǎng)絡(luò)饲梭、緩存這樣的一些基礎(chǔ)組件乘盖,分別使用不同的pods管理焰檩。
組件間通信通過ONERouter中間件進(jìn)行通信憔涉,ONERouter類似于MGJRouter,擔(dān)負(fù)起協(xié)調(diào)和調(diào)用各個組件的作用析苫。組件間通信通過OpenURL方法兜叨,來進(jìn)行對應(yīng)的調(diào)用。ONERouter內(nèi)部保存一份Class-URL的映射表衩侥,通過URL找到Class并發(fā)起調(diào)用国旷,Class的注冊放在+load方法中進(jìn)行。
滴滴在組件內(nèi)部的業(yè)務(wù)模塊中茫死,模塊內(nèi)部使用MVVM+MVCS混合架構(gòu)跪但,兩種架構(gòu)都是MVC的衍生版本。其中MVCS中的Store負(fù)責(zé)數(shù)據(jù)相關(guān)邏輯峦萎,例如訂單狀態(tài)屡久、地址管理等數(shù)據(jù)處理。通過MVVM中的VM給控制器瘦身爱榔,最后Controller的代碼量就很少了被环。
7.3、滴滴首頁分析
滴滴文章中說道首頁只能有一個地圖實(shí)例详幽,這在很多地圖導(dǎo)航相關(guān)應(yīng)用中都是這樣做的筛欢。滴滴首頁主控制器持有導(dǎo)航欄和地圖,每個業(yè)務(wù)線首頁控制器都添加在主控制器上唇聘,并且業(yè)務(wù)線控制器背景都設(shè)置為透明版姑,將透明部分響應(yīng)事件傳遞到下面的地圖中,只響應(yīng)屬于自己的響應(yīng)事件迟郎。
由主控制器來切換各個業(yè)務(wù)線首頁漠酿,切換頁面后根據(jù)不同的業(yè)務(wù)線來更新地圖數(shù)據(jù)。
8谎亩、淘寶組件化架構(gòu)
本章節(jié)源自于宗心在阿里技術(shù)沙龍上的一次分享炒嘲。
8.1宇姚、架構(gòu)發(fā)展
淘寶iOS客戶端初期是單工程的普通項目,但隨著業(yè)務(wù)的飛速發(fā)展夫凸,現(xiàn)有架構(gòu)并不能承載越來越多的業(yè)務(wù)需求浑劳,導(dǎo)致代碼間耦合很嚴(yán)重。后期開發(fā)團(tuán)隊對其不斷進(jìn)行重構(gòu)夭拌,淘寶iOS和Android兩個平臺魔熏,除了某個平臺特有的一些特性或某些方案不便實(shí)施之外,大體架構(gòu)都是差不多的鸽扁。
8.1.1蒜绽、發(fā)展歷程:
剛開始是普通的單工程項目,以傳統(tǒng)的MVC架構(gòu)進(jìn)行開發(fā)桶现。隨著業(yè)務(wù)不斷的增加躲雅,導(dǎo)致項目非常臃腫、耦合嚴(yán)重骡和。
2013年淘寶開啟"all in 無線"計劃相赁,計劃將淘寶變?yōu)橐粋€大的平臺,將阿里系大多數(shù)業(yè)務(wù)都集成到這個平臺上慰于,造成了業(yè)務(wù)的大爆發(fā)钮科。
淘寶開始實(shí)行插件化架構(gòu),將每個業(yè)務(wù)模塊劃分為一個組件婆赠,將組件以framework二方庫的形式集成到主工程绵脯。但這種方式并沒有做到真正的拆分,還是在一個工程中使用git進(jìn)行merge休里,這樣還會造成合并沖突蛆挫、不好回退等問題。
迎來淘寶移動端有史以來最大的重構(gòu)份帐,將其重構(gòu)為組件化架構(gòu)璃吧。將每個模塊當(dāng)做一個組件,每個組件都是一個單獨(dú)的項目废境,并且將組件打包成framework畜挨。主工程通過podfile集成所有組件framework,實(shí)現(xiàn)業(yè)務(wù)之間真正的隔離噩凹,通過CocoaPods實(shí)現(xiàn)組件化架構(gòu)巴元。
8.2、架構(gòu)優(yōu)勢
淘寶是使用git來做源碼管理的驮宴,在插件化架構(gòu)時需要盡可能避免merge操作逮刨,否則在大團(tuán)隊中協(xié)作成本是很大的。而使用CocoaPods進(jìn)行組件化開發(fā),則避免了這個問題修己。
在CocoaPods中可以通過podfile很好的配置各個組件恢总,包括組件的增加和刪除,以及控制某個組件的版本睬愤。使用CocoaPods的原因片仿,很大程度是為了解決大型項目中,代碼管理工具merge代碼導(dǎo)致的沖突尤辱。并且可以通過配置podfile文件砂豌,輕松配置項目。
每個組件工程有兩個target光督,一個負(fù)責(zé)編譯當(dāng)前組件和運(yùn)行調(diào)試阳距,另一個負(fù)責(zé)打包framework。先在組件工程做測試结借,測試完成后再集成到主工程中集成測試筐摘。
每個組件都是一個獨(dú)立app,可以獨(dú)立開發(fā)映跟、測試蓄拣,使得業(yè)務(wù)組件更加獨(dú)立扬虚,所有組件可以并行開發(fā)努隙。下層為上層提供能滿足需求的底層庫,保證上層業(yè)務(wù)層可以正常開發(fā)辜昵,并將底層庫封裝成framework集成到項目中荸镊。
使用CocoaPods進(jìn)行組件集成的好處在于,在集成測試自己組件時堪置,可以直接將本地主工程podfile文件中的當(dāng)前組件指向本地躬存,就可以直接進(jìn)行集成測試,不需要提交到服務(wù)器倉庫舀锨。
8.3岭洲、淘寶四層架構(gòu)
淘寶四層架構(gòu)(圖片來自淘寶技術(shù)分享)
淘寶架構(gòu)的核心思想是一切皆組件,將工程中所有代碼都抽象為組件坎匿。
淘寶架構(gòu)主要分為四層盾剩,最上層是組件Bundle(業(yè)務(wù)組件),依次往下是容器(核心層)替蔬,中間件Bundle(功能封裝)告私,基礎(chǔ)庫Bundle(底層庫)。容器層為整個架構(gòu)的核心承桥,負(fù)責(zé)組件間的調(diào)度和消息派發(fā)驻粟。
8.4、總線設(shè)計
總線設(shè)計:URL路由+服務(wù)+消息凶异。統(tǒng)一所有組件的通信標(biāo)準(zhǔn)蜀撑,各個業(yè)務(wù)間通過總線進(jìn)行通信挤巡。
總線設(shè)計(圖片來自淘寶技術(shù)分享)
URL可以請求也可以接受返回值,和MGJRouter差不多酷麦。URL路由請求可以被解析就直接拿來使用玄柏,如果不能被解析就跳轉(zhuǎn)H5頁面。這樣就完成了一個對不存在組件調(diào)用的兼容贴铜,使用戶手中比較老的版本依然可以顯示新的組件粪摘。
服務(wù)提供一些公共服務(wù),由服務(wù)方組件負(fù)責(zé)實(shí)現(xiàn)绍坝,通過Protocol實(shí)現(xiàn)徘意。消息負(fù)責(zé)統(tǒng)一發(fā)送消息,類似于通知也需要注冊轩褐。
8.5椎咧、Bundle App
Bundle App(圖片來自淘寶技術(shù)分享)
淘寶提出Bundle App的概念,可以通過已有組件把介,進(jìn)行簡單配置后就可以組成一個新的app出來勤讽。解決了多個應(yīng)用業(yè)務(wù)復(fù)用的問題,防止重復(fù)開發(fā)同一業(yè)務(wù)或功能拗踢。
Bundle即App脚牍,容器即OS,所有Bundle App被集成到OS上巢墅,使每個組件的開發(fā)就像app開發(fā)一樣簡單诸狭。這樣就做到了從巨型app回歸普通app的輕盈,使大型項目的開發(fā)問題徹底得到了解決君纫。
9驯遇、總結(jié)
9.1、留個小思考
到目前為止組件化架構(gòu)文章就寫完了蓄髓,文章確實(shí)挺長的叉庐,看到這里真是辛苦你了?
?。下面留個小思考会喝,把下面字符串復(fù)制到微信輸入框隨便發(fā)給一個好友陡叠,然后點(diǎn)擊下面鏈接大概也能猜到微信的組件化方案。
[Swift]?syntaxhighlighter_viewsource?syntaxhighlighter_copycode
01weixin://dl/profile
9.2好乐、總結(jié)
各位可以來我博客評論區(qū)討論匾竿,可以討論文中提到的技術(shù)細(xì)節(jié),也可以討論自己公司架構(gòu)所遇到的問題蔚万,或自己獨(dú)到的見解等等岭妖。無論是不是架構(gòu)師或新入行的iOS開發(fā),歡迎各位以一個討論技術(shù)的心態(tài)來討論。在評論區(qū)你的問題可以被其他人看到昵慌,這樣可能會給其他人帶來一些啟發(fā)假夺。
現(xiàn)在H5技術(shù)比較火,好多應(yīng)用都用H5來完成一些頁面的開發(fā)斋攀,H5的跨平臺和實(shí)時更新等是非常大的優(yōu)點(diǎn)已卷,但其性能和交互也是缺點(diǎn)。如果以后客戶端能夠發(fā)展到可以動態(tài)部署線上代碼淳蔼,不用打包上線應(yīng)用市場侧蘸,直接就可以做到原生應(yīng)用更新,這樣就可以解決原生應(yīng)用最大的痛點(diǎn)鹉梨。這段時間公司項目比較忙讳癌,有時間我打算研究一下這個技術(shù)點(diǎn)?
?。
Demo地址:蘑菇街和casatwy組件化方案存皂,其Github上都給出了Demo晌坤,這里就貼出其Github地址了。