iOS 組件化實(shí)踐思考

組件化的應(yīng)用背景和優(yōu)勢(shì)在此不再贅述赞警,下面我們將從實(shí)踐的角度,討論一下如何應(yīng)用組件化的思想,下面將以我自己的理解逐步展開挺峡,拋磚引玉托慨。

哪些內(nèi)容需要組件化

在我的理解中鼻由,一個(gè)項(xiàng)目可以拆分為以下幾種組件:

  • 基礎(chǔ)組件;

  • 功能組件厚棵;

  • 業(yè)務(wù)組件蕉世;

下面依次來(lái)解釋幾種組件的定義和規(guī)則。

基礎(chǔ)組件

  • 基本配置

  • 常量婆硬;

  • 宏定義狠轻;

  • 分類

  • 各種系統(tǒng)類的擴(kuò)展;

  • 網(wǎng)絡(luò)

  • 對(duì) AFN 的封裝彬犯;

  • 對(duì) SDWebImage 的封裝向楼;

  • 工具類

  • 文件處理;

  • 設(shè)備信息谐区;

  • 時(shí)間日期處理湖蜕;

基礎(chǔ)組件的含義就是最基礎(chǔ)的東西,每個(gè)業(yè)務(wù)組件都有可能會(huì)使用到宋列,基礎(chǔ)組件需要抽取的應(yīng)該是類似上面的代碼昭抒,舉例來(lái)說(shuō),比如我們定義了一個(gè)常量炼杖,表示接口的根路徑:


let BASEMIRRORURL = "http://rest.mirror.xxxx.com/ios"

那么這個(gè)常量在 Home灭返,List,Detail 都有可能會(huì)被引用坤邪,因此我們將這種最底層的熙含,最下一層的東西歸類到基礎(chǔ)組件。

又比如分類和擴(kuò)展罩扇,我們給 UIView 的擴(kuò)展定義一個(gè)計(jì)算屬性:


extension UIView {

var height {

set {

self.frame.size.height = newValue

}

get {

return self.frame.size.height

}

}

}

可以想到婆芦,也會(huì)有很多的業(yè)務(wù)組件會(huì)使用到這個(gè)擴(kuò)展。

功能組件

  • 控件

  • 彈幕喂饥;

  • 輪播消约;

  • 菜單;

  • 瀑布流员帮;

  • 功能

  • 斷點(diǎn)續(xù)傳或粮;

  • 音視頻處理;

  • CUPImage 封裝捞高;

功能組件分為可見(jiàn)和不可見(jiàn)兩種氯材,可見(jiàn)的是控件渣锦,不可見(jiàn)的是功能。功能組件的作用顧名思義氢哮,就是實(shí)現(xiàn)了一個(gè)功能袋毙。

業(yè)務(wù)組件

業(yè)務(wù)組件,也就是業(yè)務(wù)的具體實(shí)現(xiàn)了冗尤,比如一個(gè) App 的骨架如下:

  • 首頁(yè)听盖;

  • 發(fā)現(xiàn);

  • 我的裂七;

首頁(yè)下又分為這樣:

  • 側(cè)滑菜單皆看;

  • Banner;

  • 熱門背零;

這里的每個(gè)部分腰吟,都可以稱為業(yè)務(wù)組件。

三種組件的關(guān)系

三種組件的關(guān)系

基礎(chǔ)組件規(guī)則

基礎(chǔ)組件和基礎(chǔ)組件之間不應(yīng)該產(chǎn)生依賴徙瓶,比如我們使用網(wǎng)絡(luò)請(qǐng)求組件毛雇,希望根路徑是一個(gè)默認(rèn)參數(shù),但可以對(duì)外暴露和修改倍啥,像下面這樣:


class NetWork {

func request(baseUrl: String = BASEMIRRORURL, path: String, param: [String:Any]) {

}

}

NetWork.request(path: "/g/login.server", param: param)

這時(shí)禾乘,NetWork 就依賴了 常量 這個(gè)基礎(chǔ)組件,我們?nèi)绻褂?NetWork 基礎(chǔ)組件虽缕,還需要導(dǎo)入 常量 這個(gè)基礎(chǔ)組件,這是不應(yīng)該的蒲稳。

但為了代碼的簡(jiǎn)潔性氮趋,這樣的封裝又是必要的,那么應(yīng)該怎么做呢江耀?這個(gè)問(wèn)題我們下面會(huì)講到剩胁。

功能組件規(guī)則

功能組件和基礎(chǔ)組件之間不應(yīng)該產(chǎn)生依賴,比如我們做輪播圖祥国,會(huì)用到 UIView 的擴(kuò)展常量 昵观,像下面這樣:


imageView.width = SCREENWIDTH

其中 .widthSCREENWIDTH ,都在基礎(chǔ)組件中舌稀,但基礎(chǔ)組件中不僅僅是這些東西啊犬,如果依賴了基礎(chǔ)組件,就需要導(dǎo)入基礎(chǔ)組件中其他無(wú)用的代碼壁查,而且其他人使用輪播圖組件觉至,也需要導(dǎo)入基礎(chǔ)組件。

因此睡腿,在功能組件中语御,不建議依賴基礎(chǔ)組件峻贮,?上面的代碼應(yīng)該改成這樣:


imageView.frame.size.width = UIScreen.main.bounds.size.width

或者直接復(fù)制代碼,將需要的基礎(chǔ)組件的功能应闯,復(fù)制到功能組件當(dāng)中纤控。

同基礎(chǔ)組件一樣,功能組件和功能組件也不應(yīng)該產(chǎn)生依賴碉纺,道理是一樣的嚼黔,我們使用一個(gè)功能,不應(yīng)該將另一個(gè)功能也導(dǎo)入進(jìn)來(lái)惜辑。

業(yè)務(wù)組件規(guī)則

基礎(chǔ)組件和功能組件都是為業(yè)務(wù)服務(wù)的唬涧,因此業(yè)務(wù)組件可以依賴于基礎(chǔ)組件和功能組件,快速的實(shí)現(xiàn)業(yè)務(wù)盛撑,但是業(yè)務(wù)組件和業(yè)務(wù)組件之間不應(yīng)該產(chǎn)生依賴碎节。

比如這樣一條業(yè)務(wù)線,我們要求 發(fā)現(xiàn) 這個(gè)業(yè)務(wù)組件抵卫,點(diǎn)擊一條視頻狮荔,跳轉(zhuǎn)到 視頻播放器


func pushToPlayerVC(model: VideoModel) {

let vc = PlayerVC(videoModel: model)

navigationVC.push(vc)

}

這時(shí) 發(fā)現(xiàn) 就對(duì) 視頻播放器產(chǎn)生了依賴,如果將 發(fā)現(xiàn) 進(jìn)行組件化進(jìn)行剝離介粘,能行嗎殖氏?不行。

其實(shí)這個(gè)問(wèn)題和網(wǎng)絡(luò)請(qǐng)求使用默認(rèn)參數(shù)封裝一樣姻采,是組件與組件之間的通訊問(wèn)題雅采,當(dāng)然,這個(gè)問(wèn)題我們下面會(huì)講到慨亲,現(xiàn)在再提一下是為了一會(huì)兒往下寫的時(shí)候忘了填坑 ...

每個(gè)組件存在的形式

  • 組件內(nèi)部婚瓜;

  • 組件外部;

  • 組件測(cè)試刑棵;

組件內(nèi)部

組件的內(nèi)部應(yīng)該使用設(shè)計(jì)模式劃分文件夾的結(jié)構(gòu)巴刻,例如 MVVM 結(jié)構(gòu):


---- PlayerView

-- View

-- Model

-- ViewModel

組件外部

組件的外部應(yīng)該是一個(gè)遠(yuǎn)程私有 pod 庫(kù),使用 CocoaPods 進(jìn)行管理蛉签。

組件測(cè)試

單獨(dú)的測(cè)試工程胡陪。

怎樣集成各個(gè)組件

組件集成

組件的集成應(yīng)該像上面的圖一樣,基礎(chǔ)組件和功能組件互不依賴碍舍,制作遠(yuǎn)程 pod 私有庫(kù)柠座,業(yè)務(wù)組件依賴于這些 pod 私有庫(kù)開發(fā),同樣制作成遠(yuǎn)程 pod 私有庫(kù)乒验,殼工程依賴于 CocoaPods 管理這些私有庫(kù)愚隧,完成整個(gè)項(xiàng)目。

當(dāng)然還有另外的方式,比如將殼工程作為主工程狂塘,組件創(chuàng)建為子工程录煤,這方式的缺點(diǎn)是子工程可以修改,缺少約束性荞胡,目錄結(jié)構(gòu)也比較凌亂妈踊。

還有將組件制作為 FrameWork,殼工程中導(dǎo)入一個(gè)個(gè) FrameWork 庫(kù)泪漂,這種方式個(gè)人感覺(jué)比上一種好一些廊营,但是在物理上,組件和殼還是沒(méi)能做到分離萝勤。

因此露筒,我個(gè)人還是更傾向于 pod 庫(kù)的形式。

組件之間的通訊

  • 對(duì)外公開 API 接口敌卓;

  • 通過(guò)中間件的中轉(zhuǎn)慎式;

上面我們有兩個(gè)遺留的問(wèn)題,歸納為組件之間的通訊問(wèn)題趟径,下面就通過(guò)這兩個(gè)問(wèn)題瘪吏,討論一下組件之間的通訊。

網(wǎng)絡(luò)請(qǐng)求默認(rèn)參數(shù)

下面的思路就是暴露出 baseUrl 參數(shù)蜗巧,通過(guò)中間件 NetWorkMWNetWork常量 兩個(gè)基礎(chǔ)組件組合掌眠,完成默認(rèn)參數(shù)網(wǎng)絡(luò)請(qǐng)求的封裝。


// 基礎(chǔ)組件 - 常量

let BASEMIRRORURL = "http://rest.mirror.xxxx.com/ios"

// 基礎(chǔ)組件 - 網(wǎng)絡(luò)請(qǐng)求

class NetWork {

func request(baseUrl: String, path: String, param: [String:Any]) {

}

}

//殼工程 - 網(wǎng)絡(luò)請(qǐng)求中間件

class NetWorkMW {

func request(baseUrl: String = BASEMIRRORURL, path: String, param: [String:Any]) {

NetWork.request(baseUrl: baseUrl, path: path, param: param)

}

}

NetWorkMW.request(path: "/g/login.server", param: param)

發(fā)現(xiàn)跳轉(zhuǎn)視頻播放

這個(gè)思路是使用代理幕屹,對(duì)外暴露點(diǎn)擊事件蓝丙,通過(guò)中間件,導(dǎo)入 視頻播放 業(yè)務(wù)組件香嗓,topVC 基礎(chǔ)組件迅腔,完成向 視頻播放 的跳轉(zhuǎn):


// 業(yè)務(wù)組件 - 發(fā)現(xiàn)

func pushToPlayerVC(model: VideoModel) {

delegate?.pushToPlayerVC?(videoModel: model)

}

// 中間件 - 發(fā)現(xiàn)

func pushToPlayerVC(model: VideoModel) {

let vc = PlayerVC(videoModel: model)

topVC.navigationVC.push(vc)

}

以上實(shí)際上是怎么樣把多個(gè)組件組合使用起來(lái),這種組合是確定的靠娱,還有一些是不確定的,例如有一個(gè)組件的狀態(tài)改變了掠兄,我要讓其他組件知道我的變化像云,但是我不知道都要告訴誰(shuí),怎么辦蚂夕?

眼珠一轉(zhuǎn)迅诬,對(duì)外暴露狀態(tài)變化,中間件在變化時(shí)發(fā)送通知婿牍。但是同時(shí)我想附帶一個(gè)模型過(guò)去侈贷,通知的接收方怎樣正確的使用這個(gè)模型呢?如果要使用模型等脂,勢(shì)必要和發(fā)送通知的業(yè)務(wù)組件產(chǎn)生耦合俏蛮,怎么辦撑蚌?

以后再辦,先埋個(gè)坑搏屑,這些場(chǎng)景我們會(huì)在以后再講到争涌。

組件分離的難點(diǎn)

組件分離的重點(diǎn)和難點(diǎn)也就是解耦,比如我們現(xiàn)在負(fù)責(zé)一個(gè)項(xiàng)目辣恋,其中的一個(gè)業(yè)務(wù)或者功能亮垫,希望實(shí)現(xiàn)組件化,但是它依賴于項(xiàng)目中的其他公共功能伟骨,該如何處理呢饮潦?這里提供兩種思路:

  1. 拷代碼,簡(jiǎn)單粗暴携狭,擺脫依賴继蜡,對(duì)于一些不重要的工具方法,可以直接拷貝到內(nèi)部來(lái)使用暑中;

  2. 把組件依賴的代碼先做一個(gè) pod 庫(kù)壹瘟,然后依賴這個(gè) pod 庫(kù);

上面講到的是代碼方面的依賴鳄逾,還有一種情況是功能方面的依賴稻轨,比如我們有一個(gè)菜單,這個(gè)菜單涉及到網(wǎng)絡(luò)圖片的加載雕凹,那么怎樣將這個(gè)菜單進(jìn)行組件化呢殴俱?

  1. 使用 Block 或者代理,將網(wǎng)絡(luò)圖片加載這部分的職責(zé)交給外部控制枚抵;

舉例來(lái)說(shuō)线欲,像下面這樣:


// 業(yè)務(wù)組件 - 菜單

self.imageView.sd_setImage(with: url, completed: completed)

那么如果現(xiàn)在將它組件化,這個(gè)組件就要依賴于 SDWebImage汽摹,我們應(yīng)該修改成這樣:


// 業(yè)務(wù)組件 - 菜單

setImage?(for: imageView, completed: ImageLoadCompletedBlock)

// 中間件 - 菜單

menu.setImage = { (imageView, completed) in

imageView.sd_setImage(with: url, completed: completed)

}

現(xiàn)在菜單就擺脫了對(duì) SDWebImage 的依賴李丰。

附加問(wèn)題

以上的環(huán)節(jié)掌握了,應(yīng)該可以嘗試簡(jiǎn)單的組件化了逼泣,但是問(wèn)題沒(méi)完趴泌,還有哪些呢?

庫(kù)的升級(jí)維護(hù)

隨著項(xiàng)目的迭代嗜憔,你負(fù)責(zé)的庫(kù)升級(jí)了吉捶,其他的小伙伴們還在用上個(gè)版本的庫(kù)呐舔,怎么辦?

各種路徑資源問(wèn)題

我們?cè)谧约旱膸?kù)里使用了 imageNamed 榄审、mainBundle搁进,但是小伙伴把我們的庫(kù)拖過(guò)去后饼问,這些路徑和我們不是一個(gè)路徑揭斧,Assets.xcassets 跟我們也不是同一個(gè) Assets.xcassets讹开,怎么辦旦万?

這些問(wèn)題你可以從這篇文章找到答案:你真的會(huì)用 CocoaPods 嗎?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赏半,一起剝皮案震驚了整個(gè)濱河市淆两,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仲义,老刑警劉巖剑勾,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甥材,死亡現(xiàn)場(chǎng)離奇詭異洲赵,居然都是意外死亡叠萍,警方通過(guò)查閱死者的電腦和手機(jī)苛谷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門独悴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)刻炒,“玉大人自沧,你說(shuō)我怎么就攤上這事拇厢“” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵访敌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我捐顷,道長(zhǎng),這世上最難降的妖魔是什么雨效? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮徽龟,結(jié)果婚禮上叮姑,老公的妹妹穿的比我還像新娘。我一直安慰自己据悔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布极颓。 她就那樣靜靜地躺著菠隆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上清女,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼迄沫。 笑死稻扬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的羊瘩。 我是一名探鬼主播泰佳,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼尘吗!你這毒婦竟也來(lái)了逝她?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤睬捶,失蹤者是張志新(化名)和其女友劉穎黔宛,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體擒贸,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡臀晃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了介劫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徽惋。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖座韵,靈堂內(nèi)的尸體忽然破棺而出险绘,到底是詐尸還是另有隱情,我是刑警寧澤誉碴,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布宦棺,位于F島的核電站,受9級(jí)特大地震影響黔帕,放射性物質(zhì)發(fā)生泄漏代咸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一成黄、第九天 我趴在偏房一處隱蔽的房頂上張望侣背。 院中可真熱鬧白华,春花似錦、人聲如沸贩耐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)潮太。三九已至,卻和暖如春虾攻,著一層夾襖步出監(jiān)牢的瞬間铡买,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工霎箍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奇钞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓漂坏,卻偏偏與公主長(zhǎng)得像景埃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子顶别,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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