前言
當(dāng)一個App只有幾個人開發(fā)的時候氢妈,很容易就會在一個單項目中開發(fā)眼坏。但當(dāng)App開發(fā)人數(shù)越來越多,甚至幾百人地粪,十幾個不同BU都在協(xié)調(diào)開發(fā)同一個App的時候剃诅,就必須對架構(gòu)進(jìn)行組件化,才能方便開發(fā)驶忌。本文主要基于手機(jī)淘寶的一次架構(gòu)探索:手機(jī)淘寶客戶端架構(gòu)探索實踐矛辕,基于此文進(jìn)行的一些學(xué)習(xí)和探索,寫一篇文章給自己梳理一下付魔。
組件化的目的
首先聊品,第一個問題,為何需要組件化几苍?
如果依舊是單工程項目翻屈,或者是多工程引入同一個項目的開發(fā),會有以下的問題:
- 嚴(yán)重的代碼耦合
比如a模塊要跳轉(zhuǎn)b模塊的頁面妻坝,就要在a模塊的代碼中耦合b模塊的頁面代碼 - 協(xié)同工作困難
開發(fā)工程中需要去編譯別的模塊的代碼伸眶,還容易出現(xiàn)沖突問題,引發(fā)別的問題 - 測試效率低下
不僅測試某一個功能可能需要耦合別的模塊代碼刽宪,做回歸測試的時候業(yè)務(wù)也太多時分麻煩 - 發(fā)布不夠靈活
出現(xiàn)問題定位麻煩厘贼,線上熱更新也困難
為了解決這些問題,所以需要重構(gòu)代碼圣拄,達(dá)到組件化的目的嘴秸。
組件化的方式
手機(jī)淘寶組件化兩大核心:
- 分而治之
- 一些皆組件
拿一張ppt里的圖:
可以看到,手機(jī)淘寶將業(yè)務(wù)都進(jìn)行細(xì)粒度的拆分,拆分出的每一個部分都作為一個組件岳掐。每個組件可以單獨(dú)進(jìn)行測試與調(diào)試凭疮,并且確保了單一的功能性,方便在新業(yè)務(wù)接入的時候串述,可以按照需求選擇相應(yīng)的組件來進(jìn)行添加执解。
對于bundle如何組合,這里用的是CocoaPods纲酗。
CocoaPods是一個著名的iOS類庫管理工具材鹦。這里就不詳細(xì)介紹如何安裝與用CocoaPods了,感興趣的可以自己去CocoaPods Guide去看一下耕姊。
通過CocoaPods可以十分方便的選擇需要的bundle包進(jìn)入項目桶唐,并且最關(guān)鍵的是可以控制bundle包的版本號,選擇穩(wěn)定的舊版本或者新功能的老版本茉兰,避免了協(xié)同開發(fā)的時候尤泽,可能出現(xiàn)的外部問題,方便開發(fā)與測試规脸。
組件化核心層
從前面“手機(jī)淘寶組件化”的圖可以看出坯约,組件化的核心層其實是容器層。
容器層主要分為兩大塊
- 應(yīng)用生命周期
- 總線
而總線莫鸭,就是核心組件之間的解耦的關(guān)鍵闹丐。
總線主要分為三塊:
- URL
- 服務(wù)
- 消息
URL
URL應(yīng)該是整個總線傳輸?shù)暮诵摹?/strong>
模塊通過URL跳轉(zhuǎn)的的方式,來進(jìn)行模塊之間的消息傳遞被因。URL的用處是最多的卿拴,比如獲取相應(yīng)對象,跳轉(zhuǎn)相應(yīng)頁面梨与,或者發(fā)起請求堕花,都可以使用URL來進(jìn)行。
這里拿蘑菇街URL跳轉(zhuǎn)的demo:MGJRouter粥鞋,來進(jìn)行舉例子缘挽。
MGJRouter主要就是通過各個模塊注冊相應(yīng)的URL跳轉(zhuǎn)block,建立起一層URL與block的映射關(guān)系呻粹,然后調(diào)用方通過URL去訪問block壕曼,獲得結(jié)果。
通過URL的方式等浊,調(diào)用方不需要去依賴其他模塊代碼腮郊,只需要直接調(diào)用,或者獲取相應(yīng)的結(jié)果進(jìn)行處理凿掂。
比如常見的頁面跳轉(zhuǎn)問題伴榔,通過URL路由,就可以直接跳轉(zhuǎn)到相應(yīng)的頁面庄萎。
首先是注冊相應(yīng)的URL內(nèi)容踪少,這里是詳情頁的內(nèi)容。
[MGJRouter registerURLPattern:@"mgj://detail?id=:id" toHandler:^(NSDictionary *routerParameters) {
NSNumber *id = routerParameters[@"id"];
// create view controller with id
// push view controller
}];
然后調(diào)用方只需要調(diào)用
[MGJRouter openURL:@"mgj://detail?id=404"];
就可以打開相應(yīng)的界面糠涛。
不僅可以通過openURL
的方式去打開一個URL援奢,也可以通過objectForUrl
去通過URL獲取一個對象,然后進(jìn)行操作忍捡。
服務(wù)
服務(wù)用來彌補(bǔ)URL無法處理或者難以處理的功能集漾。
服務(wù)的作用主要體現(xiàn)在一些組件之間的功能調(diào)用,會比URL更佳通用砸脊。比如登陸具篇,購物車等模塊的常用功能。
服務(wù)主要通過ModuleManager
凌埂,去注冊Protocol->Class的關(guān)系定页,獲得相應(yīng)的對象耸袜,進(jìn)行Protocol的方法調(diào)用。
比如登陸組件可以提供這樣的一個Protocol
@protocol User <NSObject>
+ (NSString *)getUserName;
@end
可以看到通過協(xié)議可以直接指定返回的數(shù)據(jù)類型。然后在登陸組件內(nèi)再新建個類實現(xiàn)這個協(xié)議也切,假設(shè)這個類名為UserImp,接著就可以把它與協(xié)議關(guān)聯(lián)起來
[ModuleManager registerClass:UserImp forProtocol:@protocol(User)];
對于使用方來說老玛,要拿到這個 UserImp蜕青,需要調(diào)用
Class cls = [ModuleManager classForProtocol:@protocol(User)];
拿到之后再調(diào)用
id<User> userComponent = [[cls alloc] init];
NSString *userName = [userComponent getUserName];
就可以獲得用戶的用戶名了。
消息
消息就是常見的NSNotification相關(guān)的消息轉(zhuǎn)發(fā)機(jī)制横蜒,在這里做一個消息的統(tǒng)一管理和分發(fā)給各個模塊胳蛮,各個模塊自己去處理響應(yīng)的消息。
總線總結(jié)
總線的主要目的就是組件與組件之間的消息傳輸?shù)慕怦睢?br>
URL是總線中最主要的使用場景丛晌,包括頁面調(diào)用與發(fā)起請求等功能鹰霍。
服務(wù)是組件間的調(diào)用方式。需要服務(wù)提供方提供與維護(hù)穩(wěn)定的服務(wù)接口茵乱。
消息是在總線中進(jìn)行統(tǒng)一管理與分發(fā)茂洒。
對于URL為核心的總線機(jī)制有以下好處:
- 平臺統(tǒng)一
iOS,Android通過同一個URL總線在后臺進(jìn)行管理與配置。 - 自動降級
老版本解析不了URL瓶竭,走老的邏輯依舊可用督勺。新版本可以解析URL,走新的邏輯斤贰。 - 中心分發(fā)
業(yè)務(wù)方分別注冊自己的URL攔截規(guī)則智哀,配置在自己的模塊中。通過總線來中心分發(fā)響應(yīng)能夠響應(yīng)的模塊進(jìn)行處理荧恍。
結(jié)束語
本文主要還是根據(jù)手機(jī)淘寶客戶端架構(gòu)探索實踐和視頻瓷叫,借鑒了蘑菇街 App 的組件化之路一些具體的實現(xiàn)思路屯吊,自己整理一下思路的總結(jié)和學(xué)習(xí)。
參考資料
1.手機(jī)淘寶客戶端架構(gòu)探索實踐
2.組件化架構(gòu)漫談
3.蘑菇街 App 的組件化之路
4.iOS應(yīng)用架構(gòu)談 組件化方案