為什么會有這篇文章呢引镊?
和之前的同事"我是你爸爸"討論了關于組件化的事胸嘁,對我有很大的啟發(fā)。在此特別感謝"我是你爸爸"凹蜂。
最近寫了關于組件二進制化的文章的文章馍驯,有點感觸阁危。
一些朋友來問我關于CocoaPods的問題提到了組件化。
自己一開始準備寫《組件化之路》的博文的汰瘫,但是后來發(fā)現(xiàn)我的理解是有偏差的狂打。
以上,所以我想寫一篇關于《我所理解的組件化之路》的博文來闡述自己的觀點吟吝。
先提出一個新詞菱父,我自己想的。叫做“CocoaPods化”或叫做“l(fā)ibrary化”
什么叫做CocoaPods化剑逃?
CocoaPods化也就是我們公司正在做的浙宜。隨著業(yè)務的擴展,有了多個App蛹磺,有了多個Team粟瞬,我們希望把一些代碼重用。使用CocoaPods把他們做成library是個很好的選擇萤捆。也可以說是CocoaPods化之路裙品。
1.和業(yè)務無關。
開始做這件事的時候俗或,我們會容易的想要把那些Util市怎、Category、JSBrige等等這些和業(yè)務無關的源碼搞在一起做成一個一個CocoaPods庫辛慰。它們變成了YTXUtilCategory区匠、YTXWebViewJavaScriptBridge、YTXNibBrige帅腌、YTXAnimations這些庫驰弄。
2.弱業(yè)務
接下來,進一步地我們會把那些比如網(wǎng)絡請求速客、Server配置戚篙、行情圖、行情Socket等等這些弱業(yè)務的源碼搞在一起溺职。她們變成了YTXRequest岔擂、YTXServerId、YTXChart浪耘、YTXChartSocket智亮、YTXChatUI等等。
為什么說是弱業(yè)務呢点待,稍微分析下阔蛉。比如YTXRequest、YTXChartSocket癞埠、YTXServerId在公司內部各個App状原,各個Team之間是通用的聋呢,在各個業(yè)務組件之間可以重用和組合使用;又帶著鮮明的公司特色颠区,沒法直接開源了就能讓其他開發(fā)者使用削锰。
如果只做到了前2步,我覺得不能稱之為組件化毕莱。只能叫做CocoaPods化或Library化器贩。
3.業(yè)務
這一步,到目前來說沒有做朋截。所以沒法舉我自己實際的例子蛹稍。
比如拿美團App做例子來說。一條業(yè)務線是外賣部服,一條業(yè)務線是電影唆姐。分別由2個Team維護開發(fā)(技術,產品廓八,測試等)奉芦。有各自的KPI。這兩條業(yè)務線是自洽的剧蹂,是分治的声功。
外賣是一個業(yè)務組件,電影也是一個業(yè)務組件宠叼。里面包含了各種內容减噪,各種依賴。外賣可以手寫autolayout车吹,電影可以用storyboard。外賣可以用mvc醋闭,電影可以mvvm窄驹。想怎么搞就怎么搞。他們兩個就像獨立的App一樣证逻。
有不少朋友包括我自己之前乐埠,認為做了前2步就是組件化了。只有真正做到了第3步囚企,并且完善了相關架構丈咐,我才認為能稱之為組件化。
那么我們來看看真正的組件化應該包含什么龙宏,什么情況適合組件化棵逊。業(yè)界內部的討論已經(jīng)有很多了,我來列舉下我自己的看法银酗。
畫一個圖:
適合的情況
- 業(yè)務上要分治辆影。
- Team規(guī)模大徒像,30人+。
- 業(yè)務越來越多蛙讥,越來越大锯蛀。
如果不符合這些情況,我認為做組件化沒有意義次慢。因為性價比太低旁涤。
有一種情況表面上都符合上面列的條件,但實際上不適合組件化:例如我們公司迫像。雖然有好幾個iOS Team劈愚,雖然總人數(shù)上超過了30人,但每一個Team都只有6~10人侵蒙。每個Team各自維護各自的一個App造虎,各個App業(yè)務上沒有交集,只公用1和2步的CocoaPods庫纷闺。就算有交集算凿,做相同的業(yè)務,也不打算公用或重用這部分代碼(內部有競爭關系)犁功。
我們公司這種情況就像是拆分成了好幾個無關的小公司氓轰,大家都用了github上的一些CocoaPods庫一樣。
還好早期推了第1步和第2步浸卦,避免了每個Team之間都去造差不多功能的輪子署鸡,而能把精力盡量集中在各自的業(yè)務上,避免了一些資源浪費限嫌。
我認為需要包含什么(不分先后順序)
- App生命周期及事件如何下發(fā)給業(yè)務組件靴庆。
- 業(yè)務組件之間沒有依賴關系,需要解耦怒医。
- 解決組件化頁面跳轉的問題炉抒。
- 解決業(yè)務組件之間通信的問題。
- 解決如何劃分抽象業(yè)務組件稚叹、基礎功能組件(業(yè)務無關)和弱業(yè)務組件焰薄。
- 統(tǒng)一的網(wǎng)絡服務,本地存儲方式等扒袖。
- 去Model化塞茅。
- 如何披露接口信息,調用方式季率,參數(shù)等等野瘦。
- 明確組件的生命周期。
- 提供二進制化方案飒泻。
- 組件的subspec缅刽。
- 版本規(guī)范啊掏。
- 持續(xù)集成。
- 代碼準入制度衰猛。
- 統(tǒng)一的命名規(guī)范迟蜜。
- 集成調試。
- 代碼維護啡省。
所以我們得出的結論是:不輕易組件化娜睛。而是統(tǒng)籌規(guī)劃好以上所有的內容∝远茫可以不用一步就位全部做好畦戒,但要預先想好每一步的解決方案;能夠承上啟下结序。
如果你要問我說哪一步比較重要障斋,我覺得都挺重要的。要結合自己的實際情況徐鹤,去排一個優(yōu)先級垃环。
App生命周期及事件如何下發(fā)給業(yè)務組件
例如:applicationDidEnterBackground,didRegisterUserNotificationSettings返敬,didReceiveRemoteNotification等等遂庄。
通過注冊方式,App向注冊的業(yè)務組件中的協(xié)議發(fā)送消息劲赠。
業(yè)務組件之間沒有依賴關系涛目,需要解耦
通過依賴協(xié)議,或依賴下沉等方式解耦凛澎。準確拆分業(yè)務組件霹肝,弱業(yè)務組件,基礎功能組件塑煎。保證單一原則沫换、DRY 原則等。
解決組件化頁面跳轉的問題
各種router轧叽。比如MGJRouter。
我不建議是淡出使用URL傳參刊棕。理由是可以傳參的對象受限制炭晒。
我們自己有一套叫GOTO的東西。使用分類甥角。唯一的問題网严,你需要知道你要跳轉頁面的去model化參數(shù)是什么,代表該頁面的枚舉是什么嗤无,目前沒法注冊震束。
解決業(yè)務組件之間通信的問題
組件間需要相互調用怜庸,監(jiān)聽回調。不是說不能相互依賴么垢村?對割疾,可以通過依賴協(xié)議或中間件(依賴下沉)等方式解決這個問題。比如CTMediator嘉栓。CTMediator應該是屬于依賴下沉的方式宏榕。
解決如何劃分抽象業(yè)務組件、基礎功能組件(業(yè)務無關)和弱業(yè)務組件
這個得要從各自的實際情況出發(fā)侵佃。但有幾個原則可以借鑒:
- 重要性
- 重用性
- 單一性
統(tǒng)一的網(wǎng)絡服務麻昼,本地存儲方案等
可以通過創(chuàng)建弱業(yè)務Pod庫解決這個問題。
為什么要這么做馋辈?
Team之間人員調動后可以快速入手抚芦。
去Model化
業(yè)務組件間通訊盡量去Model化。否則就得把該Model單獨做成Pod庫迈螟。
去Model化后叉抡,比如使用NSDictionary如何及時傳播具體的參數(shù)信息?(文檔井联?口口相傳卜壕?寫在頭文件?)
如何披露接口信息烙常、調用方式轴捎、參數(shù)和一些規(guī)則等等
文檔?口口相傳蚕脏?寫在頭文件侦副?使用協(xié)議?
各有利弊和適用場景驼鞭。
按目前情況秦驯,我們選擇寫在頭文件。
明確組件的生命周期挣棕。
明確組件的生命周期译隘,就能在App中統(tǒng)一的創(chuàng)建,注冊洛心,集成固耘,協(xié)作,銷毀词身。
提供二進制化方案
二進制化方案能夠提高編譯速度厅目,提升開發(fā)效率。集中注意力在自己維護的業(yè)務組件上。
二進制化方案损敷。
組件的subspec葫笼。
subspec教程。
使用subspec可以降低集成調試門檻拗馒。集中注意力在自己維護的業(yè)務組件上路星。讓組件間依賴更清晰。
版本規(guī)范
可以參考semver瘟忱。
也可以參考我們的:
組件的依賴版本盡量寬泛一點奥额,精確到minor就行。在App里精確到patch就可以了访诱。然后大家只要按照規(guī)范發(fā)版本就可以了垫挨。參考一下這個規(guī)范。
持續(xù)集成
主要工具可以有:gitlab runner触菜,jenkins九榔,fastlne,fir.im涡相。
持續(xù)集成我們是這樣做的哲泊。
CI工具是gitlab runner。每當一定條件下催蝗,會觸發(fā)build IPA并且上傳到fir.im切威。
dev分支用的是dev證書。
master分支用的是adhoc證書丙号。
測試人員可以通過http://fir.im/TestXXApp或http://fir.im/XXApp來分別下載先朦。
.gitlab-ci.yml中的構建和上傳看起來是這樣的:
xcodebuild -exportArchive -archivePath 'build/p4.xcarchive' -exportPath 'build' -exportOptionsPlist exportOptionsDebug.plist | xcpretty
fir publish build/*.ipa -c $CI_BUILD_REF -T $FIR_TOKEN_DEBUG
在組件化開發(fā)中,一定條件應該是:
- 業(yè)務組件發(fā)版更新(會自動修改App的Podfile犬缨,然后正常push喳魏。修改的部分不只是業(yè)務組件的版本號,業(yè)務組件可能需要更高版本的其他組件或第三方組件怀薛,它會在Podfile中一并修改這些庫的版本號)
- dev/master分支正常push
- 手動觸發(fā)
代碼準入
Build/Test/Lint刺彩,code review,CI枝恋。
有了CI创倔,就可以談談代碼準入了。
- Build正常構建成功
- 單元測試通過(我們用的Kiwi)
- Lint通過
- deploymate檢查API
- OCLint檢查代碼
- CocoaPods Lint焚碌。不僅會Build一遍畦攘,還會檢查podspec相關內容設置的對不對。如果沒有用--allow-warnings的參數(shù)呐能,有waring發(fā)生Lint是會不通過的念搬。(建議把warning當作error,不要使用--allow-warings參數(shù))
- Code Review摆出。
- 檢查發(fā)版規(guī)范朗徊。比如:我們更改了一個弱業(yè)務組件,升了一個patch版本號偎漫,但其實不只是修了bug爷恳,而且還增加了向前兼容的新功能,這個時候應該升的是minor版本號象踊。
- 檢查代碼風格温亲。
- 檢查潛在的bug。
- 檢查其他只有人能看得出的問題杯矩。
.gitlab-ci.yml中的OCLint和dploymate看起來是這樣的:
Deploymate --cli -t jryMobile p4.xcworkspace -V 8.0 -x
xcodebuild -workspace p4.xcworkspace -scheme p4 -configuration Adhoc -archivePath 'build/p4' archive | tee xcodebuild.log | xcpretty
oclint-xcodebuild xcodebuild.log
oclint-json-compilation-database -e Pods -e Chart -e Chart/core/jsoncpp -e RKNotificationHub.m -e TTMessage.mm -e SSNetworkInfo.m -e Tween.mm -e 略...... && echo 'OCLint Passed' || (cat report.json && exit 1)
命名規(guī)范
公司名+組件名+具體名字
集成調試
各自業(yè)務組件如何調試栈虚?應該就和在主App中一樣,只需要在Example App中依賴相關的其他業(yè)務組件即可史隆。
另一種情況是魂务,當業(yè)務組件版本更新時需要自動修改主App的Podfile中的版本,自上而下的觸發(fā)集成泌射。
代碼維護
誰來維護基礎功能組件和弱業(yè)務組件粘姜?如何保證某個Team提交代碼后不會影響其他Team。(包含了:代碼準入熔酷,集成調試孤紧,相互協(xié)作,版本規(guī)范)
需要一個Team專門來做這個事情拒秘。
補充:寫在主App中的業(yè)務号显,要把自己當作業(yè)務組件,不能夠依賴其他業(yè)務組件翼抠。