小公司iOS客戶端代碼優(yōu)化之路:模塊化订框,組件化,動態(tài)化

疫情基本要結(jié)束了兜叨,此間有時間穿扳,我總結(jié)了一下,近兩年來我們在iOS客戶端上的代碼優(yōu)化歷程国旷。我們先后經(jīng)歷了模塊化矛物,組件化,動態(tài)化實戰(zhàn)演進(jìn)跪但。本文總結(jié)一下整體思路與所遇到的坑履羞。

先說一說,小公司何時優(yōu)化代碼屡久,為什么進(jìn)行代碼重構(gòu)忆首?

代碼深層的重構(gòu),傷筋動骨被环,在業(yè)務(wù)高速飛速發(fā)展時糙及,需要兼顧線上代碼穩(wěn)定性,同時進(jìn)行有效的迭代筛欢,對于小公司來說注重業(yè)務(wù)的發(fā)展浸锨,所以前期很可能沒有資源,做架構(gòu)上升級悴能。另一方面揣钦,一個項目如果一個人維護(hù)甚至不需要做架構(gòu),因為整個代碼即使很亂漠酿,但是只要在架構(gòu)者頭腦范圍之內(nèi)或者說此時業(yè)務(wù)還不足夠復(fù)雜到創(chuàng)作者都不能掌握冯凹,系統(tǒng)都是穩(wěn)定的,只管快速向前跑就行炒嘲。而一旦兩個人以上維護(hù)一個項目宇姚,相互交叉開發(fā)時,維護(hù)者如果不能梳理清架構(gòu)者整體的思路夫凸,亦或代碼質(zhì)量不高等原因浑劳,就會出現(xiàn)bug頻現(xiàn)的情況,而一旦出現(xiàn)bug頻現(xiàn)的情況夭拌,那就說明系統(tǒng)的可維護(hù)依然很差魔熏,維護(hù)者無法分清改動一行代碼的影響范圍衷咽。進(jìn)一步,維護(hù)者為了保證系統(tǒng)的穩(wěn)定性和自身的安全蒜绽,依然不敢對項目進(jìn)行徹底優(yōu)化镶骗,只能進(jìn)行if else搶救性開發(fā)時,導(dǎo)致系統(tǒng)進(jìn)一步的臃腫躲雅。一旦出現(xiàn)這種情況鼎姊,說明系統(tǒng)該重構(gòu)了,代碼架構(gòu)依然阻礙了系統(tǒng)穩(wěn)定性相赁,進(jìn)一步影響了業(yè)務(wù)的發(fā)展相寇。這就是代碼重構(gòu)的一個重要原因,代碼無法適應(yīng)業(yè)務(wù)的發(fā)展钮科。而本文所講的模塊化或組件化是降低代碼復(fù)雜度的有效辦法唤衫,讓業(yè)務(wù)代碼分塊隔離松耦合,分而治之跺嗽,這樣開發(fā)人員面對的代碼邏輯會有效降低战授。

這是未改造前的示例簡圖:


1.png

第一步:實現(xiàn)模塊化

模塊化,這個適合做代碼改造的開始桨嫁,這個是自上而下進(jìn)行的植兰,整體下來投入少,收益大璃吧。也是小公司最應(yīng)該實踐的楣导。

首先從業(yè)務(wù)層面考慮,涉及哪些相對獨立的業(yè)務(wù)模塊畜挨,按大的模塊進(jìn)行劃分筒繁。比方我們的APP的首頁模塊,個人中心模塊巴元,登錄模塊毡咏,信用模塊,下單模塊逮刨,還款模塊呕缭,商城模塊,會員中心都是相對獨立的模塊修己。我們把這些模塊的引用加入路由中心恢总,讓模塊都依賴路由中心,不要依賴彼此睬愤。

這是模塊化之后的示例簡圖:


2.png
業(yè)務(wù)上的收益:
  1. 實現(xiàn)運(yùn)營方利用通知或短信片仿,通過scheme,打開App尤辱,并打開指定的原生頁面或web頁面砂豌。
  2. 進(jìn)一步拓展可以實現(xiàn)更多頁面內(nèi)容的跳轉(zhuǎn)厢岂,后端配置化。
  3. 增強(qiáng)h5與原生的深度交互能力奸鸯,提升h5的更多價值咪笑。
技術(shù)上的收益:
  1. 一個模塊的代碼的進(jìn)來放在一起可帽,減少與其他模塊的引用娄涩,加強(qiáng)代碼的內(nèi)聚性。最主要解決的問題防止出現(xiàn)改一個模塊的一處代碼映跟,引發(fā)其他模塊的多個bug蓄拣,目的上減少修改代碼的影響范圍。
  2. 底層的代碼進(jìn)行抽取努隙,相互沒有任何依賴關(guān)系的庫抽離出來球恤,如工具方法,類拓展荸镊,公共UI組件咽斧,為組件化做了準(zhǔn)備。
  3. 重要模塊的跳轉(zhuǎn)以及功能都統(tǒng)一由路由中心來管理
  4. 原生頁面遇到問題躬存,可以降維到h5頁面张惹,保障業(yè)務(wù)順利進(jìn)行;拓展了與hybird的交互岭洲,實現(xiàn)支持h5與原生的混合跳轉(zhuǎn)宛逗,兩者導(dǎo)航欄的任意回退,提高了hybird的能力盾剩。
路由中心其主要承擔(dān)三部分功能:
  1. 普通的url雷激,直接push并在webview中打開。
  2. 頁面的跳轉(zhuǎn)告私,嗅探當(dāng)前的導(dǎo)航欄屎暇,進(jìn)行push或present下一個頁面。和1一起主要方便服務(wù)端動態(tài)配置驻粟。
  3. 調(diào)用獨立功能根悼,比如清空當(dāng)前導(dǎo)航棧到首頁,跳轉(zhuǎn)到導(dǎo)航棧中的指定頁面格嗅,跳轉(zhuǎn)到指定tab番挺,撥打電話,跳出APP到Appstore屯掖,等等玄柏,還有一些我們自己封裝的一些和業(yè)務(wù)無關(guān)的功能。這部分功能不少是為了h5準(zhǔn)備的贴铜。
  4. 后端接口上粪摘,配合自定義路由進(jìn)行升級瀑晒,同時將頁面信息進(jìn)一步配置化,從頁面跳轉(zhuǎn)到各種文案徘意,圖片都讓服務(wù)端下發(fā)苔悦,客戶端做好緩存策略,優(yōu)化用戶體驗椎咧。

后期維護(hù)者也比較方便玖详,比如增加兩個路由,wzry://app.wangzherongyao.com/laofuziwzry://app.wangzherongyao.com/good_test勤讽,只需要兩步走:

  1. 路由表的plist文件中增加url和對應(yīng)的target/aciton
3.png
  1. 在target對應(yīng)的類中蟋座,根據(jù)action增加對應(yīng)的方法即可。
4.png
  1. 在其他控制器中調(diào)用
result1.png

路由上的核心實現(xiàn)大家可以參考CTMediator的target-action模式

  1. 根據(jù)CTMediator的架構(gòu)脚牍,一個組件需要建一個分類并制作私有pod向臀。由于這個階段我們業(yè)務(wù)相對簡單,人員少诸狭。我們直接將url映射到一個類名和方法,runtime直接執(zhí)行類對應(yīng)的方法券膀。
  2. 另外我們的路由,綜合了更多的功能驯遇,支持分發(fā)普通的url芹彬,接管了頁面的跳轉(zhuǎn)行為,加強(qiáng)和了h5的交互

第二步:實現(xiàn)組件化

經(jīng)過模塊化妹懒,我們的代碼規(guī)整了不少雀监,穩(wěn)定性也好多了。后來有時間我們又進(jìn)行了組件化眨唬。

組件化会前,對業(yè)務(wù)上的收益來說可以利用寫好的中低層組件,更快的搭建其他功能類似的APP匾竿。技術(shù)上的收益來說瓦宜,首先代碼上的進(jìn)一步物理上的隔離,每個模塊都放在一個倉儲中岭妖,搭建私有pod來統(tǒng)一管理临庇,讓模塊與模塊之間的依賴,實現(xiàn)真正意義上的物理解耦昵慌,代碼穩(wěn)定性更加穩(wěn)定假夺。其次獨立功能封裝成了獨立組件,更方便復(fù)用斋攀,代碼在pod中已卷,就不用再使用copy文件的方式了,并且依賴什么的都不用care了淳蔼,一個pod install就搞定侧蘸。我的經(jīng)驗此時最好從下而上的進(jìn)行裁眯,因為越底層模塊,越要求穩(wěn)定性讳癌,一個底層模塊不兼容性接口的修改穿稳,可能涉及很多上層組件都要修改,特別涉及多個APP的時候晌坤。

具體實現(xiàn)上來說逢艘,在控制對業(yè)務(wù)影響的前提下,我們主要做了兩件事泡仗,一個就是路由的升級埋虹;另一個是組件的分層與粒度的劃分,其中顆粒度的劃分娩怎,根據(jù)具體的實際情況來拆分,并且隨著業(yè)務(wù)的發(fā)展可能分久必合胰柑,合久必分截亦,主要說下分層,分層相對更有參考價值柬讨。

組件的分層來說:
  • 第一層:第一步的模塊業(yè)務(wù)組件崩瓤,模塊化中涉及的獨立模塊。
  • 第二層:業(yè)務(wù)基礎(chǔ)組件踩官,這塊的組件主要用于支持上層業(yè)務(wù)組件却桶,里面包含和業(yè)務(wù)相關(guān)性較強(qiáng),如用戶模塊蔗牡,日志模塊颖系,升級模塊,web模塊辩越,人臉識別等
  • 第三層:功能組件嘁扼,和業(yè)務(wù)沒有強(qiáng)聯(lián)系,如網(wǎng)路請求組件黔攒,Debug組件趁啸,主題配置組件,設(shè)備信息組件等
  • 第四層:基礎(chǔ)組件督惰,最純粹的無相互依賴的組件不傅,主要是對第三方庫和工具類。此處說一下赏胚,由于組件化后访娶,大家對于組件會涉及很多pod install,update等栅哀,遇到網(wǎng)絡(luò)弱的情況震肮,比較耽誤時間称龙。因為公司沒有g(shù)ithub的鏡像或cache,我們依賴不是很多戳晌,就采用了最笨的方法鲫尊,大家每人幾個把庫搬到公司自己的gitlab上,搬得過程中使用mirror也很方便沦偎。

下圖為組件化分層疫向,示例簡圖:


5.png
路由的升級

我們結(jié)合模塊化中的經(jīng)驗對路由進(jìn)行了標(biāo)準(zhǔn)化升級。搭建了面向協(xié)議的路由中心豪嚎,組件對外暴漏的方法action搔驼,都抽像為協(xié)議。這樣做的一方面是實現(xiàn)對組件方法的明晰和規(guī)范侈询,各個組件的所有協(xié)議可以放到一起便于查閱舌涨,使用pod進(jìn)行統(tǒng)一的管理,大家對組件依賴這個協(xié)議庫就好扔字,按照設(shè)計的原則囊嘉,高層模塊不應(yīng)該直接依賴底層模塊,而應(yīng)該依賴兩者的接口革为。另一方面內(nèi)部組件的調(diào)用可以直接使用協(xié)議的方式扭粱,防止了沒必要的url解析過程;并且內(nèi)部組件調(diào)用更多震檩,如果每個調(diào)用方法都設(shè)置url琢蛤,會產(chǎn)生很多url,url本身可讀性不強(qiáng)抛虏,有不少局限性博其。所以最后我們使用protocal的方式。

具體實現(xiàn)上來說嘉蕾,以前是直接通過路由表贺奠,直接找到target-acion并執(zhí)行,組件只需要實現(xiàn)一個方法就好了错忱;現(xiàn)在來說儡率,下面的組件要實現(xiàn)一個協(xié)議方法,并且要提前通過load方法將自己的類信息和滿足的協(xié)議注冊到router中以清。

現(xiàn)在來看看具體的添加過程:

1.路由表為兼容以前的target-action儿普,在老的plist文件中增加一個protocal鍵值對。新的直接使用省去target鍵值對掷倔。

6.png

2.定義一個協(xié)議

7.png

3.實現(xiàn)協(xié)議眉孩,并掛載到路由中心

8.png

9.png

4.具體調(diào)用,可以使用url方式,也可以使用協(xié)議的方式

10.png

第三步:實現(xiàn)動態(tài)化

經(jīng)過組件化后浪汪,整個項目從上到下巴柿,已經(jīng)處于相對松耦合的狀態(tài)。此時我們正好業(yè)務(wù)上需要動態(tài)化死遭。動態(tài)化業(yè)務(wù)上收益在于無需等待發(fā)版就可以更新APP广恢;技術(shù)上,雖然我們通過testflight實現(xiàn)了代碼的灰度測試呀潭,但是有些問題是大量用戶才能暴露的問題钉迷,這時隨時修復(fù)線上bug的功能就非常有必要了。我們選擇了使用reactnative钠署,性能比hybird的方式好一些糠聪。而在組件化的基礎(chǔ)上,對單個模塊進(jìn)行動態(tài)化谐鼎,對整個項目的影響降到了最小舰蟆。當(dāng)然也要付出代價拿出資源來踩坑,解決技術(shù)上的問題该面。新啟一個以RN主導(dǎo)的項目相對好一些夭苗。但是如果涉及混合開發(fā),特別是在原有項目中隔缀,增加RN模塊的這種情況,就會遇到一些問題傍菇。我們的路徑是先拿一個全新的項目的用RN來做猾瘸,后面再加到現(xiàn)有穩(wěn)定項目中。我們也在探索的階段丢习,就簡單說遇到的問題與解決辦法牵触。

  1. 剛開始的時候,根據(jù)RN腳手架構(gòu)建出的項目咐低,不支持cocopod安裝依賴揽思,使用pod方式安裝組件的時候,就遇到各種編譯錯誤见擦。所幸的是0.60.0后钉汗,rn支持了pod方式的注入,0.61.0后更是支持use_framework鲤屡,方便了很多损痰。
  2. 混合開發(fā)時,第一個是網(wǎng)絡(luò)請求的問題酒来,使用RN來發(fā)送還是原生發(fā)送卢未,最后我們選擇原生發(fā)送。一方面我們測試使用原生頁面發(fā)送請求比RN發(fā)送請求請求,速度快很多辽社;另一方面伟墙,因為RN只是整個項目中某一個模塊的一部分,使用一套底層的網(wǎng)絡(luò)框架滴铅,如用戶狀態(tài)戳葵,數(shù)據(jù)解析都不單獨處理。這樣RN頁面只需要處理頁面相關(guān)的邏輯即可失息。
  3. 混合開始時需要原生頁面與RN頁面的相互跳轉(zhuǎn)譬淳,這個時候就需要考慮RN頁面承載的問題,我們采用類咸魚的flutter_boost的方案盹兢,即在原生頁面打開一個rn頁面時邻梆,使用一個viewcontroller作為rn容器,rn頁面的跳轉(zhuǎn)都在這個容器內(nèi)進(jìn)行绎秒。
  4. 打包與升級更新的問題浦妄。打包系統(tǒng)需要進(jìn)行升級支持RN的打包,同時升級時為應(yīng)對動態(tài)的大小與安全問題见芹,需要進(jìn)行了diff運(yùn)算剂娄,保證下發(fā)的最小的包,同時支持全包更新與回滾玄呛。

RN總體使用下來阅懦,最好還是和原生搭配使用,感覺性能還是其發(fā)展的瓶頸徘铝,js的單線程不能高效的處理并發(fā)問題耳胎,同樣一個請求,我們用原生發(fā)送請求回傳給RN頁面惕它,比使用RN自己請求回來怕午,要快10倍以上。另外在混合開發(fā)時淹魄,會遇到很多莫名奇妙的問題郁惜,需要不斷的填坑,這可能也是跨平臺的代價吧甲锡。

總結(jié)

我們開始的模塊化就是在業(yè)務(wù)邏輯越復(fù)雜兆蕉,代碼耦合嚴(yán)重,bug不斷增多的情況下搔体,進(jìn)行了代碼的重構(gòu)恨樟。后面的組件化時是我們有足夠時間情況下,進(jìn)行的代碼預(yù)先升級疚俱,為后面的業(yè)務(wù)做準(zhǔn)備劝术。當(dāng)然如果一個項目一開始就以整潔的代碼要求自己,可能后面不需要傷筋動骨的改造;并且代碼的優(yōu)化是有各個維度的养晋,任何時候都可以開始衬吆,只要你是有心人∩總之架構(gòu)上之改進(jìn)逊抡,即可以是業(yè)務(wù)受影響,技術(shù)上更新?lián)Q代的戰(zhàn)略反擊零酪;也可以是主動出擊冒嫡,占領(lǐng)優(yōu)勢地位,為后續(xù)更大的戰(zhàn)斗創(chuàng)造機(jī)會的提前布局四苇,預(yù)則立不預(yù)則廢孝凌。我感覺小公司至少要實現(xiàn)模塊化,一是業(yè)務(wù)上的需要月腋,二是為組件化做準(zhǔn)備蟀架。不必操之過急的去實現(xiàn)組件化,因為組件化之后榆骚,必然需要一些額為的維護(hù)工作片拍,還需要一些自動化的配套設(shè)施來輔助才好,比如我們搞了組件的驗證與上傳腳本妓肢,打包發(fā)版本自動化構(gòu)建捌省。中大公司自然要會去做組件化,團(tuán)隊人比較多碉钠,代碼耦合越來越嚴(yán)重所禀,協(xié)作的時間遠(yuǎn)大于組件化維護(hù)的時間,這個時候組件化可以顯著提升業(yè)務(wù)上開發(fā)效率和穩(wěn)定性放钦。至于動態(tài)化更是看業(yè)務(wù)需要,綜合考慮性能等各種因素恭金,來統(tǒng)一規(guī)劃操禀。

參考文章:

手機(jī)淘寶客戶端架構(gòu)探索實踐

iOS應(yīng)用架構(gòu)談 組件化方案

蘑菇街 App 的組件化之路

模塊化與解耦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市横腿,隨后出現(xiàn)的幾起案子颓屑,更是在濱河造成了極大的恐慌,老刑警劉巖耿焊,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揪惦,死亡現(xiàn)場離奇詭異,居然都是意外死亡罗侯,警方通過查閱死者的電腦和手機(jī)器腋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纫塌,你說我怎么就攤上這事诊县。” “怎么了措左?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵依痊,是天一觀的道長。 經(jīng)常有香客問我怎披,道長胸嘁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任凉逛,我火速辦了婚禮性宏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鱼炒。我一直安慰自己衔沼,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布昔瞧。 她就那樣靜靜地躺著指蚁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪自晰。 梳的紋絲不亂的頭發(fā)上凝化,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機(jī)與錄音酬荞,去河邊找鬼搓劫。 笑死,一個胖子當(dāng)著我的面吹牛混巧,可吹牛的內(nèi)容都是我干的枪向。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼咧党,長吁一口氣:“原來是場噩夢啊……” “哼秘蛔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起傍衡,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤深员,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蛙埂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倦畅,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年绣的,在試婚紗的時候發(fā)現(xiàn)自己被綠了叠赐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欲账。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖燎悍,靈堂內(nèi)的尸體忽然破棺而出敬惦,到底是詐尸還是另有隱情,我是刑警寧澤谈山,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布俄删,位于F島的核電站,受9級特大地震影響奏路,放射性物質(zhì)發(fā)生泄漏畴椰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一鸽粉、第九天 我趴在偏房一處隱蔽的房頂上張望斜脂。 院中可真熱鬧,春花似錦触机、人聲如沸帚戳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽片任。三九已至,卻和暖如春蔬胯,著一層夾襖步出監(jiān)牢的瞬間对供,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工氛濒, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留产场,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓舞竿,卻偏偏與公主長得像京景,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子骗奖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容