iOS 組件化1-cocopods spec

理論篇

什么是組件化

組件化開(kāi)發(fā)就是將一個(gè)臃腫的褪储、單一的項(xiàng)目滓走,根據(jù)功能/業(yè)務(wù)/技術(shù)等等進(jìn)行拆分权薯,形成一個(gè)個(gè)獨(dú)立的功能組件讼呢,然后借助 CocoaPods 管理工具將其任意組合撩鹿,集成一個(gè)完整的項(xiàng)目。

你可以將 AFNetworking悦屏、SDWebImage 等等三方庫(kù)理解為自己項(xiàng)目的一部分节沦,屬于基礎(chǔ)組件部分,而我們要做的就是將項(xiàng)目劃分成多個(gè)獨(dú)立功能模塊窜管,再集成為一個(gè)完整的項(xiàng)目散劫。這一過(guò)程看似多此一舉稚机,但是帶來(lái)的優(yōu)勢(shì)卻是非常大幕帆。

為什么需要組件化

項(xiàng)目初期,功能相對(duì)簡(jiǎn)單赖条,普通的MVC+模塊文件分割就可以滿(mǎn)足絕大部分的需求失乾。但是隨著功能需求越來(lái)越多,業(yè)務(wù)越來(lái)越復(fù)雜多樣纬乍,現(xiàn)有的架構(gòu)已經(jīng)不太適用了碱茁,即使使用了 Git 分支管理,依然經(jīng)常發(fā)生合并沖突等等問(wèn)題仿贬,另外后期的維護(hù)成本也大大增加纽竣,業(yè)務(wù)邏輯變得復(fù)雜、模塊之間耦合度很大、查找問(wèn)題效率變低蜓氨、項(xiàng)目編譯過(guò)程過(guò)慢…… 而且伴隨著開(kāi)發(fā)人員的增多(多個(gè)小組之間協(xié)作開(kāi)發(fā))聋袋,這些問(wèn)題尤為突出,優(yōu)化開(kāi)發(fā)結(jié)構(gòu)變得非常重要穴吹。

中間層

針對(duì)上面的問(wèn)題幽勒,第一個(gè)想到的優(yōu)化就是新增一個(gè)中間層來(lái)協(xié)調(diào)各個(gè)模塊之間的調(diào)用,所有的模塊都通過(guò)這個(gè)中間層去實(shí)現(xiàn)調(diào)用和交互港令,但是這樣雖然一定程度上降低了模塊與模塊的之間的耦合度啥容,但是耦合都轉(zhuǎn)嫁到了中間層上了,并且中間層的改動(dòng)只能由一個(gè)人操作顷霹,否則非常容易發(fā)生沖突咪惠,本質(zhì)上并沒(méi)有發(fā)生多少變化。另外一點(diǎn)淋淀,查找問(wèn)題的效率低下硝逢、編譯過(guò)慢等問(wèn)題依舊沒(méi)有得到有效的解決。

這是傳統(tǒng)的中介者模式绅喉,這個(gè)中間層會(huì)依賴(lài)其他組件渠鸽,其他組件也會(huì)依賴(lài)中間層完成服務(wù)。

組件化

組件化能夠幫助我們將過(guò)大的項(xiàng)目拆解成數(shù)個(gè)小組件柴罐,開(kāi)發(fā)者只需要關(guān)注于組件所依賴(lài)的其他組件徽缚,而無(wú)需關(guān)心完整項(xiàng)目的其他部分,每個(gè)組件可以自己采取所習(xí)慣的架構(gòu)模式:MVC革屠、MVVM凿试、MVCS等等,就像開(kāi)發(fā)一款個(gè)人獨(dú)立的app那樣自由似芝。

項(xiàng)目組件化之后所帶來(lái)的好處是非常多的那婉,我們先總結(jié)一下非組件化所造成的問(wèn)題:

非組件化:

  • 代碼高耦合度、高依賴(lài)
  • 項(xiàng)目復(fù)雜党瓮、臃腫详炬、編譯過(guò)長(zhǎng)(影響調(diào)試)
  • 難以融合/集成其他產(chǎn)品
  • 需要統(tǒng)一架構(gòu)
    ……

組件化:

  • 代碼復(fù)用性提高,可方便的集成到其他項(xiàng)目
  • 項(xiàng)目可配置寞奸,方便集成和功能回退(指定版本)
  • 化整為零呛谜,將項(xiàng)目細(xì)小化
  • 方便組件的并行開(kāi)發(fā)
  • 可方便做單元測(cè)試
  • 組件自由度高,即插即用
    ……

當(dāng)然組件化也有著它的缺點(diǎn)枪萄,對(duì)已有的項(xiàng)目實(shí)施組件化架構(gòu)比較困難隐岛,耗費(fèi)時(shí)間長(zhǎng),項(xiàng)目組成員需要一定學(xué)習(xí)成本瓷翻;組件化并沒(méi)有相應(yīng)的標(biāo)準(zhǔn)聚凹,拆分的粒度要適中割坠,拆分粒度過(guò)高,則讓項(xiàng)目變得復(fù)雜妒牙,起到了反作用效果韭脊,反之,粒度過(guò)低单旁,體現(xiàn)不了組件化的優(yōu)勢(shì)沪羔,在項(xiàng)目業(yè)務(wù)不斷地添加的過(guò)程中,進(jìn)行不斷的嘗試調(diào)整象浑,找到適合自己項(xiàng)目的才是最好的蔫饰。

組件化的分層

項(xiàng)目組件化中,最難把握的就是粒度問(wèn)題愉豺,這需要開(kāi)發(fā)的自己的經(jīng)驗(yàn)去把控篓吁。這里只給出個(gè)人認(rèn)為的層次的劃分。

【基礎(chǔ)組件】:宏定義/常量/自定義工具類(lèi)蚪拦,如常用的自定義分類(lèi)
【功能組件】:項(xiàng)目中所用到的功能杖剪,如地圖定位/消息推送/分享等
【業(yè)務(wù)組件】:項(xiàng)目中的模塊/業(yè)務(wù),如聊天室/直播間/個(gè)人中心等
【中間組件】:負(fù)責(zé)項(xiàng)目中的路由/消息通知/傳參/回調(diào)等
【宿主工程】:項(xiàng)目容器驰贷,用來(lái)集成組件盛嘿,調(diào)整各個(gè)組件之間的消息傳遞的容器。

中間層的幾種方案

在組件化過(guò)程中括袒,中間層是各個(gè)組件的通信的橋梁次兆,中間層在組件化過(guò)程中扮演著非常重要的角色。目前關(guān)于中間層的設(shè)計(jì)筆者已知的有以下三種方式:基于URL Scheme的路由锹锰、基于Runtimetarget-action芥炭、面向接口。

  • 路由

iOS 中支持的 URL Scheme 讓我們能夠在應(yīng)用之間恃慧、應(yīng)用內(nèi)部傳遞消息园蝠。日常開(kāi)發(fā)過(guò)程中經(jīng)常用到的就是調(diào)用系統(tǒng)服務(wù)、喚起三方app等等痢士,這些屬于應(yīng)用之間的消息傳遞彪薛,而我們這里借助 URL Scheme 完成應(yīng)用內(nèi)部的消息傳遞。這里的路由 URL 遵循網(wǎng)上通用的資源標(biāo)識(shí)符合 URI良瞧,如:appscheme://home/scan?param=value陪汽,我們通過(guò) URL 來(lái)傳遞信息,下層服務(wù)方通過(guò) URL 獲取參數(shù)提供服務(wù)褥蚯,上層消費(fèi)者通過(guò) URL 獲取到服務(wù),完成調(diào)用况增。

基于 URL Scheme 的三方庫(kù)

JLRoutes 是一種基于 URL Scheme 的路由框架赞庶,它全局會(huì)保存一個(gè)Map,key 是路由協(xié)議 url,value 則是對(duì) url 解析后 block 回調(diào)歧强,你可以在該回調(diào)中處理具體的業(yè)務(wù)澜薄。

實(shí)例:

例如我們的路由協(xié)議定義如下:

scheme://描述/打開(kāi)方式/保留字段/功能標(biāo)識(shí)?參數(shù)1=值1&參數(shù)2=值2
||
myroute://market/1/route/cjpm?stockcode=600212.ss

首先配置路由 url 和 對(duì)應(yīng)的回調(diào)處理:

/// 默認(rèn)下都會(huì)進(jìn)入這里摊册,這里填寫(xiě)路由匹配規(guī)則
[JLRoutes.globalRoutes addRoute:@"/market/:operate/route/:code" handler:^BOOL(NSDictionary<NSString *,id> * _Nonnull parameters) {
    NSLog(@"%@", parameters);
    // 接下來(lái)的業(yè)務(wù)邏輯
    return YES; // 返回YES肤京,表示處理截止,后面的路由規(guī)則不再啟用
}];

然后在需要路由的地方傳入相應(yīng)的路由 url :

// 某地方獲取到的url
NSURL* url = [NSURL URLWithString:@"myroute://market/1/route/cjpm?stockcode=600212.ss"];
// 處理路由
[JLRoutes routeURL:url];

基于Runtimetarget-action

相比 url scheme 的提前注冊(cè)茅特、實(shí)現(xiàn)服務(wù)忘分,CTMediator 借助 OC 運(yùn)行時(shí)的特性,現(xiàn)實(shí)組件之間服務(wù)的自動(dòng)發(fā)現(xiàn)白修,無(wú)需提前注冊(cè)即可實(shí)現(xiàn)組件間的調(diào)用妒峦,因此,這種方案的可維護(hù)性兵睛、可讀性肯骇、擴(kuò)展性相對(duì)較高。

官方的 Demo 中祖很,結(jié)構(gòu)是這樣的:

image

CTMediator 的使用流程大體是這樣的:

底層組件

  1. 創(chuàng)建 Target_ 開(kāi)頭的目標(biāo)類(lèi)笛丙,如Target_A(該類(lèi)是為了讓中間件 CTMedator 通過(guò) NSClassFromString生成類(lèi)),類(lèi)中定義 Action_ 開(kāi)頭的可調(diào)用的方法(為了讓中間件 CTMedator通過(guò) NSSelectorFromString 生成方法器)假颇,并且這些方法都有一個(gè)字典類(lèi)型參數(shù)接收調(diào)用者傳遞過(guò)來(lái)的信息若债。

  2. 創(chuàng)建 CTMedator 的分類(lèi)(方便擴(kuò)展、分塊)拆融,此分類(lèi)對(duì)應(yīng)著Target_A蠢琳,分類(lèi)中定義該組件對(duì)外(調(diào)用者)開(kāi)放的 API 方法,該組件的開(kāi)發(fā)者需要使用 CTMedator 的核心方法 performTarget:action:params:shouldcacheTarget: 完成方法調(diào)用镜豹。

上層組件

導(dǎo)入對(duì)應(yīng) CTMedator 的分類(lèi)傲须,完成方法調(diào)用。

相比傳統(tǒng)的中介者模式趟脂,這種 target-action 方案解放了中間件對(duì)其他組件的依賴(lài)泰讽,因?yàn)樗峭ㄟ^(guò) NSClassFromStringNSSelectorFromString 來(lái)生成類(lèi)的實(shí)例和方法器SEL的,然后介入消息的分發(fā)機(jī)制完成消息分發(fā)的昔期,即所謂的主動(dòng)發(fā)現(xiàn)服務(wù)已卸。傳統(tǒng)的中介者模式中,中間件和其他組件是雙向依賴(lài)的:

image

target-action 方式則是單向依賴(lài)硼一,這樣做的一個(gè)好處就是降低了一定的耦合累澡,在我們移除某個(gè)組件時(shí),中間件無(wú)需進(jìn)行改動(dòng)般贼。

image

那么愧哟,由于沒(méi)有引入具體的類(lèi)奥吩,而是通過(guò)字符生成對(duì)應(yīng)的類(lèi)和方法,那么關(guān)于 CTMedator 的分類(lèi)要清楚的知道 Target_ 類(lèi)以及其中的內(nèi)容蕊梧。

CTMedator 的分類(lèi)可以劃分為一個(gè)組件霞赫,必要時(shí),集成到項(xiàng)目中進(jìn)行調(diào)用肥矢。

  • 面向接口 Protocol

在路由和 target-action 方案中端衰,都存在硬編碼問(wèn)題、參數(shù)不明確問(wèn)題:URL 甘改、Target_ 旅东、 Action_ 的硬編碼,參數(shù)都是通過(guò)字典的形式傳遞楼誓,類(lèi)型不明確玉锌。

面向接口 的方式能夠很好的解決這兩個(gè)問(wèn)題。面向接口的方案通常由兩部分組成疟羹,一個(gè)是用來(lái)管理接口協(xié)議的類(lèi)(ModuleManager)主守,一個(gè)是具體的接口協(xié)議(ComponentProtocol)。

ModuleManager 負(fù)責(zé)消息的調(diào)用和轉(zhuǎn)發(fā)榄融,它內(nèi)部需要存儲(chǔ)一張映射表参淫,完成 Protocol -> Class 的工作。ComponentProtocol 文件定義了業(yè)務(wù)組件可以提供的功能服務(wù)愧杯,可以將所有服務(wù)都定義到其中涎才,也可以按組件劃分。這樣所有調(diào)用方只需要依賴(lài)中間件力九,不需要依賴(lài)其他的組件耍铜,而中間件通過(guò)接口協(xié)議綁定可以用于服務(wù)的類(lèi),即每個(gè)組件有一個(gè)用于實(shí)現(xiàn)對(duì)外提供的接口協(xié)議的類(lèi)跌前。在編譯時(shí)棕兼,將對(duì)應(yīng)的類(lèi)注冊(cè)到ModuleManager 中,Protocol 的名稱(chēng)即為查找的 key抵乓。

注冊(cè)綁定:

[ModuleManager registerClass:User forProtocol:@protocol(UserProtocol)];

調(diào)用時(shí)通過(guò)接口協(xié)議從 ModuleManager 中映射出注冊(cè)的 Class伴挚,將獲取到的 Class 實(shí)例化,并調(diào)用協(xié)議方法完成服務(wù)調(diào)用灾炭。

Class cls = [[ModuleManager sharedInstance] classForProtocol:@protocol(UserProtocol)];
NSObject <UserProtocol> *user = [[cls alloc] init];
NSString *userName = [user getUserName];

接口協(xié)議的方式雖然可以很好的解決參數(shù)類(lèi)型的不確定性茎芋,硬編碼問(wèn)題(實(shí)現(xiàn)部分可以任意替換),但是它不是前面兩種的替代品蜈出,因?yàn)樗麄兌加凶约旱膫?cè)重點(diǎn)田弥,如 路由URL 可以在應(yīng)用之間實(shí)現(xiàn)消息傳遞,面向接口可以用來(lái)為某類(lèi)添加功能或者對(duì)類(lèi)進(jìn)行功能約束等掏缎。

一些注入框架是支持面向接口的注入的皱蹦,可使用這些庫(kù)取代 ModuleManager 類(lèi)煤杀。

小結(jié)

三種方式都分為底層服務(wù)方和上層使用方眷蜈,服務(wù)方都對(duì)外提供 了服務(wù)媒介沪哺,CTMediator 中是 Target_A 文件,面向接口就是 Protocol酌儒,路由 URL Scheme 則是回調(diào) block辜妓。

在三種方式中,個(gè)人覺(jué)得最不推薦的是 CTMediator 方案忌怎,感覺(jué)很是臃腫,雖然可以通過(guò)多個(gè)分類(lèi)去定義組件,但是實(shí)際上對(duì)底層組件的調(diào)用邏輯都耦合在了中間件中酒繁,這意味著中間件需要頻繁的進(jìn)行更新膳凝,另外存在太多的硬編碼地方,target鸥印、action以及參數(shù)名都是硬編碼在中間件中的勋功,這樣的方式并不靈活。但是 CTMediator 中通過(guò)運(yùn)行時(shí)解耦了中間件對(duì)底層組件的依賴(lài)库说,以及去 model 化的想法還是非常好的狂鞋。

面向接口 Protocol 的方案貫穿了底層組件、中間件以及上層組件潜的,一方面解耦了中間件對(duì)底層組件的耦合骚揍,底層組件變得透明,可以根據(jù)接口協(xié)議任意替換啰挪,另一方面接口協(xié)議還確定了參數(shù)類(lèi)型信不。但是該方案面向的是應(yīng)用內(nèi)部的功能通信,外部調(diào)用應(yīng)用時(shí)亡呵,還是需要路由或者硬編碼的形式完成抽活。

路由定義了一套用于信息傳遞的標(biāo)準(zhǔn),通過(guò)路由政己,服務(wù)方可以注冊(cè)并實(shí)現(xiàn)符合某種特定條件的服務(wù)酌壕,使用方則通過(guò)中間件傳遞 一條URL 來(lái)調(diào)用該服務(wù)。服務(wù)方和使用方彼此透明歇由,可以任意替換卵牍。和接口協(xié)議比起來(lái),路由的可以處理本地內(nèi)部和遠(yuǎn)程外部的兩種類(lèi)型的調(diào)用沦泌,缺點(diǎn)是 url 需要硬編碼糊昙,而且參數(shù)類(lèi)型都是字符。路由 URL 和接口協(xié)議都需要提前注冊(cè)才能使用谢谦,路由需要 block释牺,接口協(xié)議需要 class萝衩。

路由和接口協(xié)議并不沖突,可以使用路由 + 協(xié)議的方式來(lái)實(shí)現(xiàn)中間件没咙,路由實(shí)現(xiàn)外部的調(diào)用猩谊,應(yīng)用的降級(jí)處理等,組件之間通過(guò)接口協(xié)議來(lái)定義功能服務(wù)祭刚,這樣組件內(nèi)部可以在迭代中方便的替換實(shí)現(xiàn)類(lèi)牌捷。

核心工具 CocoaPods

組件化架構(gòu),需要一個(gè)宿主工程涡驮,負(fù)責(zé)集成所有的組件暗甥。每個(gè)組件都是一個(gè)單獨(dú)的工程,通過(guò) Git 私有倉(cāng)庫(kù)來(lái)管理捉捅。這樣拆分工程項(xiàng)目撤防,開(kāi)發(fā)人員只需要關(guān)注與組件相關(guān)的部分,而不用考慮其他組件棒口,新人上手更容易寄月。

image

所有的組件都上傳到 Git 倉(cāng)庫(kù)并支持 Cocoapods 集成。主工程通過(guò)配置 Podfile 文件陌凳,然后一鍵 pod update 即可剥懒。使用 Cocoapods 來(lái)管理組件主要因?yàn)槠浔旧砉δ軓?qiáng)大,方便的集成整個(gè)項(xiàng)目合敦,解放對(duì)依賴(lài)庫(kù)的管理初橘。使用組件化的集成方式,可以很好的避免傳統(tǒng)項(xiàng)目中的代碼沖突問(wèn)題充岛。

核心命令:

# 安裝命令
sudo gem install cocoapods
# 配置
pod setup
# 通過(guò)Podfile安裝三方庫(kù)
pod install
# 通過(guò)Podfile更新安裝三方庫(kù)
pod update

Git 是一個(gè)分布式版本控制系統(tǒng)保檐,能夠快速高效地處理從小型到大型項(xiàng)目的所有內(nèi)容。Git 官方文獻(xiàn)資料崔梗。

當(dāng)然夜只,如果不想記住這些命令,你可以借助市場(chǎng)上的熱門(mén)開(kāi)發(fā)工具蒜魄,這里推薦 Git 官方桌面端扔亥、Sourcetree

Xcode 本身就支持項(xiàng)目的 Git 倉(cāng)庫(kù)管理谈为,在 Source control 中就可以創(chuàng)建管理你的項(xiàng)目旅挤。

image

在你創(chuàng)建項(xiàng)目時(shí),Xcode 就提示你是否創(chuàng)建 Git 倉(cāng)庫(kù):

image

這里需要注意的就是 podspec 索引文件的編寫(xiě)伞鲫。

Pod::Spec.new do |s|
 s.name             = '組件工程名'
 s.version          = '0.0.1'
 s.summary          = '簡(jiǎn)介'
 s.homepage         = '遠(yuǎn)程倉(cāng)庫(kù)地址'
 s.license          = { :type => 'MIT', :file => 'LICENSE' }
 s.author           = { '作者' => '作者' }
 s.source           = { :git => '遠(yuǎn)程倉(cāng)庫(kù)地址', :tag => s.version }
 s.ios.deployment_target = '8.0'
 s.source_files     = 'Classes/**/*.{swift,h,m,c}'
 s.resources        = 'Assets/*'
 s.dependency 'AFNetworking', '~> 2.3'
 s.dependency 'Reachability','~> 3.2
end

source_files:是你要共享的文件
resources:是一些資源文件粘茄,比如圖片資源
dependency:是該組件所需要依賴(lài)的其他組件、三方組件等。

關(guān)于創(chuàng)建子模塊/子文件夾

 //簡(jiǎn)單:
    subspec 'Twitter' do |sp|
        sp.source_files = 'Classes/Twitter' //指定子模塊路徑
    end
    subspec 'Pinboard' do |sp|
        sp.source_files = 'Classes/Pinboard'
    end

    //復(fù)雜:
    Pod::Spec.new do |s|
        s.name = 'RestKit'

        s.subspec 'Core' do |cs|
            cs.dependency 'RestKit/ObjectMapping'
            cs.dependency 'RestKit/Network'
            cs.dependency 'RestKit/CoreData'
        end
        s.subspec 'ObjectMapping' do |os|
        end
    end

更多內(nèi)容參考 基礎(chǔ)-podSpec使用柒瓣。

典型的產(chǎn)品

  • 滴滴

滴滴的組件化是將項(xiàng)目拆分為業(yè)務(wù)部分和技術(shù)部分儒搭,業(yè)務(wù)部分包括專(zhuān)車(chē)、拼車(chē)芙贫、巴士等組件搂鲫,使用一個(gè) pods 管理,技術(shù)部分則分為登陸分享屹培、網(wǎng)絡(luò)默穴、緩存等基礎(chǔ)組件怔檩,分別使用不同的 pods 管理褪秀。

組件間的通信通過(guò) ONERouter 中間件進(jìn)行通信,中間件擔(dān)負(fù)協(xié)調(diào)和調(diào)用各個(gè)組件的責(zé)任薛训。組件間通信通過(guò) OpenURL 方法來(lái)進(jìn)行對(duì)應(yīng)的調(diào)用媒吗。ONERouter 內(nèi)部保存一份 Class - URL 的映射表,通過(guò) URL 找到 Class 并發(fā)起調(diào)用乙埃, Class 的注冊(cè)放在 +load 中進(jìn)行闸英。

  • 淘寶

淘寶架構(gòu)的核心思想是一切皆組件,將工程中所有代碼都抽象為組件介袜。在 CocoaPods 中可以通過(guò) podfile 很好的配置各個(gè)組件甫何,包括組件的增加和刪除,以及控制某個(gè)組件的版本遇伞。

淘寶架構(gòu)的主要分為四層辙喂,從上到下依次是:業(yè)務(wù)組件 -> 核心層 /容器-> 中間件/功能封裝 -> 底層庫(kù)。容器是整個(gè)架構(gòu)的核心鸠珠,負(fù)責(zé)組件間的調(diào)度和消息派發(fā)巍耗。

總線(xiàn)設(shè)計(jì):URL 路由 + 服務(wù) + 消息。統(tǒng)一所有組件的通信標(biāo)準(zhǔn)渐排,各個(gè)業(yè)務(wù)間通過(guò)總線(xiàn)進(jìn)行通信炬太。

URL 路由

路由 URL 統(tǒng)一對(duì)三端的行為進(jìn)行了統(tǒng)一,一套 URL 就可以調(diào)起 iOS驯耻、Android亲族、前端三個(gè)平臺(tái)的對(duì)應(yīng)組件。

URL 路由請(qǐng)求可以被解析就直接調(diào)起相應(yīng)的組件可缚,如果不能被解析(沒(méi)有對(duì)應(yīng)的組件)就跳轉(zhuǎn) H5 頁(yè)面霎迫,這稱(chēng)為降級(jí)處理。

服務(wù)

服務(wù)提供一些公共服務(wù)城看,是面向接口的女气,通過(guò)接口協(xié)議 Protocol 進(jìn)行調(diào)用。

消息

URL 路由通常都是一對(duì)一進(jìn)行通信测柠,那么針對(duì)一對(duì)多的消息派發(fā)和調(diào)度就可以通過(guò)消息完成炼鞠,這類(lèi)似于 iOS 的通知機(jī)制缘滥。例如應(yīng)用的前后臺(tái)切換、Socket的推送消息等谒主,都可以通過(guò)消息機(jī)制分發(fā)到接收消息的組件朝扼。

小結(jié)

我們可以看到,滴滴和淘寶的組件化上有很多的相似之處霎肯,組件化的核心工具 CocoaPods擎颖,URL 路由進(jìn)行頁(yè)面的路由跳轉(zhuǎn),其他的如接口協(xié)議观游、消息通知等搂捧,應(yīng)該都有類(lèi)似的解決方法。除了管理組件的核心 CocoaPods 工具懂缕,URL 路由允跑、接口協(xié)議服務(wù)、消息通知等都是我們?cè)诮M件化過(guò)程中使用到的利器搪柑。

總結(jié)

組件化開(kāi)發(fā)就是將項(xiàng)目進(jìn)行拆分成一個(gè)個(gè)獨(dú)立的功能組件聋丝,然后將其組合成一個(gè)完整的項(xiàng)目。那么工碾,如何拆分弱睦?組件如何通信?如何組合渊额?都是我們要考慮的問(wèn)題况木。關(guān)于分層和拆分的粒度都沒(méi)有標(biāo)準(zhǔn)化的,需要開(kāi)發(fā)者根據(jù)以往已經(jīng)合理的進(jìn)行規(guī)范端圈。組件間的通信有多種方式焦读,這里比較推崇淘寶的架構(gòu),路由 + 服務(wù) + 消息的形式實(shí)現(xiàn)多種方式的通信舱权。組件化的核心工具就是 CocoaPods 矗晃,我們要做的就是將組件項(xiàng)目上傳到 Git 或者碼云,編寫(xiě)項(xiàng)目的 podSpec 文件讓組件支持 CocoaPods 集成 即可宴倍。CocoaPods 的功能十分強(qiáng)大张症,即使非組件化項(xiàng)目,我們同樣使用它來(lái)管理依賴(lài)庫(kù)鸵贬,安裝俗他、卸載、升級(jí)阔逼、降級(jí)等兆衅,只需要一個(gè)命令即可完成,作為開(kāi)發(fā)者,這個(gè)工具是必定要掌握的羡亩。

參考


實(shí)踐篇

上一章節(jié)中摩疑,我們簡(jiǎn)單介紹了以下組件化的概念、使用到的工具等畏铆,這一章節(jié)中我們來(lái)演示一個(gè)組件如何制作雷袋。

組件的創(chuàng)建

首先我們來(lái)為項(xiàng)目創(chuàng)建一個(gè)關(guān)于網(wǎng)絡(luò)請(qǐng)求的功能組件 LLNetworking

  • 拉取模版

我們將創(chuàng)建在桌面上的一個(gè)名為 Demo 文件夾中辞居。通過(guò)終端進(jìn)入到該文件夾下楷怒,然后輸入命令:

pod lib create LLNetworking

這個(gè)命令會(huì)為了拉取 Pod 的 基礎(chǔ)模板。拉取之后瓦灶,還會(huì)通過(guò)詢(xún)問(wèn)的形式為你配置一些東西:

// 作用的平臺(tái)
What platform do you want to use?? [ iOS / macOS ]
 > iOS

// 語(yǔ)言環(huán)境
What language do you want to use?? [ Swift / ObjC ]
 > ObjC

// 是否需要一個(gè) demo 用來(lái)測(cè)試組件
Would you like to include a demo application with your library? [ Yes / No ]
 > Yes

Which testing frameworks will you use? [ Specta / Kiwi / None ]
 > None

Would you like to do view based testing? [ Yes / No ]
 > Yes

// 組件中鸠删,文件的前綴
What is your class prefix?
 > LL

確認(rèn)之后,系統(tǒng)會(huì)為你自動(dòng)配置組件項(xiàng)目倚搬,創(chuàng)建好的項(xiàng)目如下:

image
  • Example 工程

項(xiàng)目文件目錄中存在一個(gè)名為 Example 的工程冶共,這個(gè)工程是你選擇 Would you like to include a demo application with your library? 中選擇 Yes 時(shí)為你添加的,這個(gè)還是很有用的每界,在你開(kāi)發(fā)過(guò)程中可以通過(guò)它來(lái)集成測(cè)試組件功能的正確性、完整性家卖。 我們先打開(kāi)這個(gè) Example 來(lái)看下:

image

這個(gè) Example 已經(jīng)為你的組件創(chuàng)建了索引文件 podspec眨层,并且集成了該組件。我們來(lái)看下 ExamplePodfile 的內(nèi)容:

use_frameworks!

platform :ios, '8.0'

target 'LLNetworking_Example' do
  pod 'LLNetworking', :path => '../'

  target 'LLNetworking_Tests' do
    inherit! :search_paths

    pod 'FBSnapshotTestCase'
  end
end

其中為你集成了一個(gè)測(cè)試用例 pod 'FBSnapshotTestCase'上荡,目前可以忽略趴樱。

我們可以看到: pod 'LLNetworking', :path => '../' 這一句,path 路徑指向了本地路徑酪捡,對(duì)應(yīng) LLNetworking 主目錄下:

image

這個(gè)文件夾下叁征,一個(gè)存放你的各種類(lèi)文件,一個(gè)存放圖片資源等逛薇。

  • podspec 文件

在你回答之前問(wèn)題之后捺疼,pod 為你自動(dòng)創(chuàng)建了該文件,并執(zhí)行了 pod install 命令永罚,該命令會(huì)找到組件的索引文件(也在本地) LLNetworking.podspec

#
# Be sure to run `pod lib lint LLNetworking.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#

Pod::Spec.new do |s|
  s.name             = 'LLNetworking'
  s.version          = '0.1.0'
  s.summary          = 'A short description of LLNetworking.'

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!

  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC

  s.homepage         = 'https://github.com/LOLITA0164/LLNetworking'
  # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'LOLITA0164' => '476512340@qq.com' }
  s.source           = { :git => 'https://github.com/LOLITA0164/LLNetworking.git', :tag => s.version.to_s }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

  s.ios.deployment_target = '8.0'

  s.source_files = 'LLNetworking/Classes/**/*'

  # s.resource_bundles = {
  #   'LLNetworking' => ['LLNetworking/Assets/*.png']
  # }

  # s.public_header_files = 'Pod/Classes/**/*.h'
  # s.frameworks = 'UIKit', 'MapKit'
  # s.dependency 'AFNetworking', '~> 2.3'
end

該文件為你的組件自動(dòng)配置了一些基本的信息啤呼,因?yàn)槲抑笆褂眠^(guò) trunk 登陸過(guò),所以這里有的的賬號(hào)信息呢袱。當(dāng)然這些信息是需要你根據(jù)情況修改的官扣,更多的配置你可以搜索相關(guān)文檔。

注意:這里的 Git 地址目前是找不到的羞福,后期需要自己關(guān)聯(lián)惕蹄。

設(shè)置共享文件

podspec 文件中 s.source_files = 'LLNetworking/Classes/**/*' 指代共享的資源路徑,我們需要將共享的文件放到這里來(lái)。

image

我們打開(kāi)組件的目錄查看卖陵,可以看到這里已經(jīng)有了名為 ReplaceMe 的文件了恋昼,這暗示你用共享文件替換它。

podspec 文件中還有一個(gè)被注釋掉的:

# s.resource_bundles = {
  #   'LLNetworking' => ['LLNetworking/Assets/*.png']
  # }

這個(gè)目錄中存放一些圖片等資源赶促,當(dāng)你需要的時(shí)候可以開(kāi)啟來(lái)液肌。

我們來(lái)創(chuàng)建一個(gè) LLNetworking 類(lèi):

@interface LLNetworking : NSObject
-(NSString*)getSomething;
@end

@implementation LLNetworking
-(NSString *)getSomething{
    return @"test method.";
}
@end

將其移動(dòng)到組件的共享目錄下并刪除掉空文件ReplaceMe

image

這樣,我們就設(shè)置好了共享的內(nèi)容鸥滨,即組件開(kāi)發(fā)好了嗦哆。接下來(lái),我們使用 Example 工程來(lái)使用這個(gè)組件的內(nèi)容婿滓。

終端進(jìn)入 Example 工程目錄下老速,執(zhí)行 pod install 命令來(lái)安裝組件。

image

我們發(fā)現(xiàn)凸主,Example 項(xiàng)目中 Pods/Development Pods/LLNetworking 下橘券,多出來(lái)最新添加的文件。

使用組件

我們安裝好組件之后來(lái)使用一下組件的功能卿吐,就像使用三方庫(kù)那樣:

-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    LLNetworking * networking = LLNetworking.new;
    NSLog(@"%@",networking.getSomething);
}

控制臺(tái)輸出:

2019-11-08 17:14:47.455341+0800 LLNetworking_Example[7038:1682304] test method.

這表示功能正常旁舰。

在組件開(kāi)發(fā)過(guò)程中,使用 pod 'LLNetworking', :path => '../' 將路徑指向本地是很有必要的嗡官,方便測(cè)試你的組件配置是否正確箭窜,功能是否完善,相比推到遠(yuǎn)程衍腥、發(fā)布再集成磺樱,這方便太多了。

三方依賴(lài)庫(kù)

有時(shí)候婆咸,我們的組件還依賴(lài)其他的組件竹捉,又或者是三方庫(kù)。我們通過(guò) s.dependency 字段去設(shè)置尚骄,多個(gè)庫(kù)可以分開(kāi)寫(xiě)多次块差。

在 Podfiles 模版里最后一條已經(jīng)為我們添加好了,所依賴(lài)的是 AFNetworking 乖仇,正好是我們網(wǎng)絡(luò)請(qǐng)求組件所依賴(lài)的憾儒,我們把它開(kāi)啟,重新 pod install :

Analyzing dependencies
Fetching podspec for `LLNetworking` from `../`
Downloading dependencies
Installing AFNetworking (2.7.0)
……

我們發(fā)現(xiàn)乃沙,Example 自動(dòng)拉取了組件 LLNetworking 所依賴(lài)的其他組件起趾。CocoaPods 工具的另外一個(gè)優(yōu)點(diǎn)就是,多個(gè)組件依賴(lài)同一個(gè)組件時(shí)警儒,它會(huì)自動(dòng)幫你檢測(cè)安裝训裆,而不會(huì)重復(fù)導(dǎo)入眶根。

image

我們發(fā)現(xiàn) Example 工程的 Pods中,本地開(kāi)發(fā)的組件和遠(yuǎn)程發(fā)布的組件被分別放在了不同的目錄下边琉。

有了 AFNetworking 之后属百,你就可以修改你的網(wǎng)絡(luò)請(qǐng)求組件了:

#import <AFNetworking/AFNetworking.h>
@interface LLNetworking : NSObject
@property(strong,nonatomic)NSURLSessionDataTask *task;
- (NSURLSessionDataTask *)POSTWithURLString:(NSString *)URLString parameters:(id)parameters success:(void (^)(id responseObject))success failure:(void (^)(id error))failure;
@end

@implementation LLNetworking
- (NSURLSessionDataTask *)POSTWithURLString:(NSString *)URLString parameters:(id)parameters success:(void (^)(id responseObject))success failure:(void (^)(id error))failure{
   AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
   manager.responseSerializer = [AFHTTPResponseSerializer serializer];
   manager.requestSerializer.timeoutInterval = 20;
  _task =  [manager POST:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
      if (success) {
          success(@{@"status":@"success"});
      }
   } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
       if (failure) {
           failure(@{@"status":@"failure"});
       }
   }];
   return _task;
}
@end

修改好之后,還不能直接在 Example 中使用变姨,需要卸載組件再重新安裝族扰。注釋掉 pod 'LLNetworking', :path => '../' 之后執(zhí)行 pod install 即可完成卸載。

至此定欧,你完成了組件的創(chuàng)建渔呵、文件共享、本地化測(cè)試使用和更新砍鸠。但是扩氢,我們的組件畢竟是要服務(wù)于宿主工程的,如果僅僅只能是通過(guò)本地集成爷辱,那意義不大录豺,我們要將其關(guān)聯(lián)到遠(yuǎn)程服務(wù)器,推送到本地搭建的 GitLab饭弓,又或者是 GitHub双饥、碼云、Coding 等平臺(tái)示启。
關(guān)聯(lián)遠(yuǎn)程倉(cāng)庫(kù)

在模版 podspec 文件中兢哭,已經(jīng)幫我們指定了一個(gè) GitHub 的倉(cāng)庫(kù)地址,

s.homepage         = 'https://github.com/LOLITA0164/LLNetworking'

你可以使用它或者進(jìn)行修改它夫嗓。我們這里選擇使用它,先去 GitHub 創(chuàng)建對(duì)應(yīng)的倉(cāng)庫(kù)冲秽。

image

在最初創(chuàng)建組件時(shí)舍咖,系統(tǒng)已經(jīng)幫我們創(chuàng)建好了本地 Git 倉(cāng)庫(kù),進(jìn)入到項(xiàng)目中锉桑,顯示出隱藏文件夾就可以看到(command+shift+. 顯隱):

image

如果沒(méi)有排霉,你可以使用命令 git init 創(chuàng)建一個(gè)。現(xiàn)在民轴,我們要將之前的修改進(jìn)行提交(本地提交)攻柠。

git commit -am "第一次提交"  

然后我們要把本地的 Git 倉(cāng)庫(kù)和剛剛創(chuàng)建的遠(yuǎn)程倉(cāng)庫(kù)進(jìn)行關(guān)聯(lián)。如何關(guān)聯(lián)呢后裸?你在網(wǎng)站上創(chuàng)建項(xiàng)目后有了這樣的提示:

image

這里有三種:創(chuàng)建一個(gè)新的倉(cāng)庫(kù)瑰钮,推送一個(gè)已存在的倉(cāng)庫(kù)以及從其他倉(cāng)庫(kù)導(dǎo)入。我們這里使用第二種即可微驶。

添加遠(yuǎn)程倉(cāng)庫(kù):

git remote add origin https://github.com/LOLITA0164/LLNetworking.git

將本地內(nèi)容推送到遠(yuǎn)程倉(cāng)庫(kù):

git push -u origin master

可能會(huì)出現(xiàn)讓你登陸驗(yàn)證浪谴,輸入你的用戶(hù)名和密碼即可开睡。出現(xiàn)以下信息即表示推送成功。

remote: Resolving deltas: 100% (49/49), done.
To https://github.com/LOLITA0164/LLNetworking.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

回到 GitHub 刷新一下即可看到你的提交記錄苟耻。

上述是通過(guò)終端命令進(jìn)行 git 操作篇恒,如果你并不熟悉 git 命令,你大可以使用便捷的可視化工具(上一章節(jié)有所提及)凶杖,僅需簡(jiǎn)單的點(diǎn)擊操作即可完成項(xiàng)目的管理胁艰。
打 tag 并發(fā)布到 Cocoapods

打標(biāo)簽

至此,我們已經(jīng)成功的將本地倉(cāng)庫(kù)關(guān)聯(lián)并推送到遠(yuǎn)程倉(cāng)庫(kù)智蝠,現(xiàn)在我們要發(fā)布一個(gè)可用的組件腾么。

首先我們要給當(dāng)前項(xiàng)目打一個(gè) tag 版本號(hào),在 podspec 中:

s.version          = '0.1.0'

指定的版本號(hào)是 0.1.0寻咒,那么我們就同樣打個(gè) 0.1.0 的 tag:

$ git tag 0.1.0  // 打 tag
$ git push --tags  // 推送到遠(yuǎn)程

tag 默認(rèn)在當(dāng)前分支上哮翘,因?yàn)檫@里只有 master,所以不用切換分支毛秘,如果后期有其他分支饭寺,注意別弄錯(cuò)了。

刷新頁(yè)面叫挟,項(xiàng)目的 release 選項(xiàng)中會(huì)出現(xiàn)剛剛打的版本艰匙。

image

你也可以直接在頁(yè)面的 release 下添加新的 tag,點(diǎn)擊 release 可以看到編輯頁(yè)面:

image

發(fā)布到 CocoaPods

由于我們創(chuàng)建的項(xiàng)目以及標(biāo)簽的版本號(hào)都是沿用了 podspec 文件中的信息抹恳,因此可以直接驗(yàn)證 podspec 文件信息是否可以通過(guò)驗(yàn)證员凝,如果需要調(diào)整,調(diào)整之后最好同樣先驗(yàn)證:

pod spec lint

podspec 文件的版本號(hào)一定要和 tag 保持一致奋献。

如果通過(guò)驗(yàn)證健霹,那么你會(huì)看到類(lèi)似下面的提示,綠色的 passed validation

image

現(xiàn)在可以將 podspec 文件提交到 CocoaPods 上了:

首先要通過(guò) trunk 注冊(cè)生成一條會(huì)話(huà):

// pod trunk register 郵箱 用戶(hù)名 描述
pod trunk register 476512340@qq.com LOLITA0164 --description=組件化demo 

然后去郵箱進(jìn)行驗(yàn)證瓶蚂,驗(yàn)證成功會(huì)出現(xiàn)下面頁(yè)面:

image

現(xiàn)在糖埋,就可以將 podspec 提交給 CocoaPods 了。這個(gè)文件將是別人搜索你的組件的索引窃这。

pod trunk push LLNetworking.podspec --allow-warnings

上傳完成之后瞳别,接可以通過(guò) pod search LLNetworking 搜索到自己的組件了,如果搜索不到杭攻,刪除本地的搜索文件祟敛,命令 :

rm ~/Library/Caches/CocoaPods/search_index.json

重新 search 產(chǎn)生新的搜索文件。

發(fā)布新版本則需要打新的 tag兆解,重新編輯 podspec 文件馆铁,然后再次提交給 CocoaPods

集成到宿主工程

我們已經(jīng)完成了網(wǎng)絡(luò)組件的創(chuàng)建和發(fā)布痪宰,也支持了 CocoaPods 的集成〉鸺埽現(xiàn)在我們需要將該組件集成到宿主工程中去畔裕,這部分沒(méi)什么好提的,因?yàn)槭褂梅绞胶图扇綆?kù)是一樣的乖订,可以說(shuō)三方庫(kù)只不過(guò)是他人編寫(xiě)的功能組件而已扮饶,我們的組件同樣可以提供給小組成員使用,相比于純粹的三方庫(kù)乍构,我們的許多組件都關(guān)聯(lián)了業(yè)務(wù)部分甜无,或者基于私人的其他組件,因此適用范圍較小哥遮。

小結(jié)

本章節(jié)先介紹了如何通過(guò) pod 的模版工程創(chuàng)建組件岂丘,組件的配置,集成本地組件眠饮,然后介紹了遠(yuǎn)程倉(cāng)庫(kù)的關(guān)聯(lián)奥帘,支持 CocoaPods 的集成等內(nèi)容,學(xué)會(huì)了這些仪召,你就可以將自己得意的功能庫(kù)提供給他人使用寨蹋。在組件化的過(guò)程中,Git 是我們必須要掌握的扔茅,即使你不會(huì)使用命令已旧,但是一定要熟悉相關(guān)的軟件。

原文鏈接:http://www.reibang.com/p/4d129ea43926

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末召娜,一起剝皮案震驚了整個(gè)濱河市运褪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌玖瘸,老刑警劉巖秸讹,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異雅倒,居然都是意外死亡嗦枢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)屯断,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人侣诺,你說(shuō)我怎么就攤上這事殖演。” “怎么了年鸳?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵趴久,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我搔确,道長(zhǎng)彼棍,這世上最難降的妖魔是什么灭忠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮座硕,結(jié)果婚禮上弛作,老公的妹妹穿的比我還像新娘。我一直安慰自己华匾,他們只是感情好映琳,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蜘拉,像睡著了一般萨西。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旭旭,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天谎脯,我揣著相機(jī)與錄音,去河邊找鬼持寄。 笑死源梭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的际看。 我是一名探鬼主播咸产,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼仲闽!你這毒婦竟也來(lái)了脑溢?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赖欣,失蹤者是張志新(化名)和其女友劉穎屑彻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體顶吮,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡社牲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悴了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搏恤。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖湃交,靈堂內(nèi)的尸體忽然破棺而出熟空,到底是詐尸還是另有隱情,我是刑警寧澤搞莺,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布息罗,位于F島的核電站,受9級(jí)特大地震影響才沧,放射性物質(zhì)發(fā)生泄漏迈喉。R本人自食惡果不足惜绍刮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挨摸。 院中可真熱鬧孩革,春花似錦、人聲如沸油坝。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)澈圈。三九已至彬檀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瞬女,已是汗流浹背窍帝。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诽偷,地道東北人坤学。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像报慕,于是被迫代替她去往敵國(guó)和親深浮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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

  • 理論篇 什么是組件化 組件化開(kāi)發(fā)就是將一個(gè)臃腫的眠冈、單一的項(xiàng)目飞苇,根據(jù)功能/業(yè)務(wù)/技術(shù)等等進(jìn)行拆分,形成一個(gè)個(gè)獨(dú)立的功...
    小和大大閱讀 2,890評(píng)論 0 5
  • 理論篇 什么是組件化 組件化開(kāi)發(fā)就是將一個(gè)臃腫的蜗顽、單一的項(xiàng)目布卡,根據(jù)功能/業(yè)務(wù)/技術(shù)等等進(jìn)行拆分,形成一個(gè)個(gè)獨(dú)立的功...
    小白進(jìn)城閱讀 2,148評(píng)論 0 11
  • 寂靜海岸(加載圖).jpg更新時(shí)間:2022-6-22增加了參數(shù)回調(diào)的說(shuō)明雇盖,并列舉可以通過(guò)字典方式傳遞閉包然后進(jìn)行...
    麻辣檸檬閱讀 12,163評(píng)論 14 50
  • 一忿等、前言 什么是組件化 組件化就是將APP拆分成各個(gè)組件,然后通過(guò)主工程將項(xiàng)目所需要的組件組合起來(lái)崔挖,比如首頁(yè)贸街,個(gè)人...
    _Andy_閱讀 2,162評(píng)論 2 12
  • 最近在學(xué)習(xí)vue.js的時(shí)候發(fā)現(xiàn),vue的組件化的思想對(duì)于編寫(xiě)代碼是一個(gè)非常有用的事情狸相。 首先為什么需要組件化匾浪? ...
    拂曉的云閱讀 7,182評(píng)論 6 23