我所理解的組件化之路

為什么會有這篇文章呢?

和之前的同事"我是你爸爸"討論了關于組件化的事,對我有很大的啟發(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在公司內(nèi)部各個App,各個Team之間是通用的,在各個業(yè)務組件之間可以重用和組合使用;又帶著鮮明的公司特色,沒法直接開源了就能讓其他開發(fā)者使用。

如果只做到了前2步,我覺得不能稱之為組件化。只能叫做CocoaPods化或Library化。

3.業(yè)務
這一步,到目前來說沒有做。所以沒法舉我自己實際的例子。

比如拿美團App做例子來說。一條業(yè)務線是外賣,一條業(yè)務線是電影。分別由2個Team維護開發(fā)(技術,產(chǎn)品,測試等)。有各自的KPI。這兩條業(yè)務線是自洽的,是分治的。

外賣是一個業(yè)務組件,電影也是一個業(yè)務組件。里面包含了各種內(nèi)容,各種依賴。外賣可以手寫autolayout,電影可以用storyboard。外賣可以用mvc,電影可以mvvm。想怎么搞就怎么搞。他們兩個就像獨立的App一樣。

有不少朋友包括我自己之前,認為做了前2步就是組件化了。只有真正做到了第3步,并且完善了相關架構(gòu),我才認為能稱之為組件化。

那么我們來看看真正的組件化應該包含什么,什么情況適合組件化。業(yè)界內(nèi)部的討論已經(jīng)有很多了,我來列舉下我自己的看法。

畫一個圖:


default
default

適合的情況

  • 業(yè)務上要分治。
  • Team規(guī)模大,30人+。
  • 業(yè)務越來越多,越來越大。

如果不符合這些情況,我認為做組件化沒有意義。因為性價比太低。

有一種情況表面上都符合上面列的條件,但實際上不適合組件化:例如我們公司。雖然有好幾個iOS Team,雖然總?cè)藬?shù)上超過了30人,但每一個Team都只有6~10人。每個Team各自維護各自的一個App,各個App業(yè)務上沒有交集,只公用1和2步的CocoaPods庫。就算有交集,做相同的業(yè)務,也不打算公用或重用這部分代碼(內(nèi)部有競爭關系)。

我們公司這種情況就像是拆分成了好幾個無關的小公司,大家都用了github上的一些CocoaPods庫一樣。

還好早期推了第1步和第2步,避免了每個Team之間都去造差不多功能的輪子,而能把精力盡量集中在各自的業(yè)務上,避免了一些資源浪費。

我認為需要包含什么(不分先后順序)

  • App生命周期及事件如何下發(fā)給業(yè)務組件。
  • 業(yè)務組件之間沒有依賴關系,需要解耦。
  • 解決組件化頁面跳轉(zhuǎn)的問題。
  • 解決業(yè)務組件之間通信的問題。
  • 解決如何劃分抽象業(yè)務組件、基礎功能組件(業(yè)務無關)和弱業(yè)務組件。
  • 統(tǒng)一的網(wǎng)絡服務,本地存儲方式等。
  • 去Model化。
  • 如何披露接口信息,調(diào)用方式,參數(shù)等等。
  • 明確組件的生命周期。
  • 提供二進制化方案。
  • 組件的subspec。
  • 版本規(guī)范。
  • 持續(xù)集成。
  • 代碼準入制度。
  • 統(tǒng)一的命名規(guī)范。
  • 集成調(diào)試。
  • 代碼維護。

所以我們得出的結(jié)論是:不輕易組件化。而是統(tǒng)籌規(guī)劃好以上所有的內(nèi)容??梢圆挥靡徊骄臀蝗孔龊?,但要預先想好每一步的解決方案;能夠承上啟下。

如果你要問我說哪一步比較重要,我覺得都挺重要的。要結(jié)合自己的實際情況,去排一個優(yōu)先級。

App生命周期及事件如何下發(fā)給業(yè)務組件

例如:applicationDidEnterBackground,didRegisterUserNotificationSettings,didReceiveRemoteNotification等等。
通過注冊方式,App向注冊的業(yè)務組件中的協(xié)議發(fā)送消息。

業(yè)務組件之間沒有依賴關系,需要解耦

通過依賴協(xié)議,或依賴下沉等方式解耦。準確拆分業(yè)務組件,弱業(yè)務組件,基礎功能組件。保證單一原則、DRY 原則等。

解決組件化頁面跳轉(zhuǎn)的問題

各種router。比如MGJRouter
我不建議是淡出使用URL傳參。理由是可以傳參的對象受限制。

我們自己有一套叫GOTO的東西。使用分類。唯一的問題,你需要知道你要跳轉(zhuǎn)頁面的去model化參數(shù)是什么,代表該頁面的枚舉是什么,目前沒法注冊。

解決業(yè)務組件之間通信的問題

組件間需要相互調(diào)用,監(jiān)聽回調(diào)。不是說不能相互依賴么?對,可以通過依賴協(xié)議或中間件(依賴下沉)等方式解決這個問題。比如CTMediator。CTMediator應該是屬于依賴下沉的方式。

解決如何劃分抽象業(yè)務組件、基礎功能組件(業(yè)務無關)和弱業(yè)務組件

這個得要從各自的實際情況出發(fā)。但有幾個原則可以借鑒:

  • 重要性
  • 重用性
  • 單一性

統(tǒng)一的網(wǎng)絡服務,本地存儲方案等

可以通過創(chuàng)建弱業(yè)務Pod庫解決這個問題。

為什么要這么做?
Team之間人員調(diào)動后可以快速入手。

去Model化

業(yè)務組件間通訊盡量去Model化。否則就得把該Model單獨做成Pod庫。

去Model化后,比如使用NSDictionary如何及時傳播具體的參數(shù)信息?(文檔?口口相傳?寫在頭文件?)

如何披露接口信息、調(diào)用方式、參數(shù)和一些規(guī)則等等

文檔?口口相傳?寫在頭文件?使用協(xié)議?

各有利弊和適用場景。

按目前情況,我們選擇寫在頭文件。

明確組件的生命周期。

明確組件的生命周期,就能在App中統(tǒng)一的創(chuàng)建,注冊,集成,協(xié)作,銷毀。

提供二進制化方案

二進制化方案能夠提高編譯速度,提升開發(fā)效率。集中注意力在自己維護的業(yè)務組件上。
二進制化方案。

組件的subspec。

subspec教程。
使用subspec可以降低集成調(diào)試門檻。集中注意力在自己維護的業(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/TestXXApphttp://fir.im/XXApp來分別下載。

.gitlab-ci.yml中的構(gòu)建和上傳看起來是這樣的:

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,就可以談談代碼準入了。

  1. Build正常構(gòu)建成功
  2. 單元測試通過(我們用的Kiwi
  3. Lint通過
  4. deploymate檢查API
  5. OCLint檢查代碼
  6. CocoaPods Lint。不僅會Build一遍,還會檢查podspec相關內(nèi)容設置的對不對。如果沒有用--allow-warnings的參數(shù),有waring發(fā)生Lint是會不通過的。(建議把warning當作error,不要使用--allow-warings參數(shù))
  7. Code Review。
    1. 檢查發(fā)版規(guī)范。比如:我們更改了一個弱業(yè)務組件,升了一個patch版本號,但其實不只是修了bug,而且還增加了向前兼容的新功能,這個時候應該升的是minor版本號。
    2. 檢查代碼風格。
    3. 檢查潛在的bug。
    4. 檢查其他只有人能看得出的問題。

.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ī)范

公司名+組件名+具體名字

集成調(diào)試

各自業(yè)務組件如何調(diào)試?應該就和在主App中一樣,只需要在Example App中依賴相關的其他業(yè)務組件即可。

另一種情況是,當業(yè)務組件版本更新時需要自動修改主App的Podfile中的版本,自上而下的觸發(fā)集成。

代碼維護

誰來維護基礎功能組件和弱業(yè)務組件?如何保證某個Team提交代碼后不會影響其他Team。(包含了:代碼準入,集成調(diào)試,相互協(xié)作,版本規(guī)范)

需要一個Team專門來做這個事情。

補充:寫在主App中的業(yè)務,要把自己當作業(yè)務組件,不能夠依賴其他業(yè)務組件。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容