前言
本篇主要講解組件化架構(gòu)思想,從零教你如何組件化一個項目。
如果喜歡我的文章,可以關(guān)注我微博:袁崢Seemygo
為什么要組件化
- 隨著項目的不斷迭代,各個模塊會越來越復(fù)雜,各個模塊相互依賴,而且每個模塊可能會有共同的業(yè)務(wù)邏輯,導(dǎo)致整個項目維護(hù)起來比較麻煩。
- 可以采用組件化,把每個業(yè)務(wù)邏輯和模塊分離,單獨管理,這樣比較方便維護(hù),各個開發(fā)人員只需要關(guān)心好自己的模塊就好了。
組件化好處
- 分工更加明確,提高開發(fā)效率
- 復(fù)用性更好,能迅速的組成更多的App
組件化思想
- 就好比封裝控件,復(fù)雜的控件一般都會封裝,組件化只不過是把每個模塊單獨抽出來,作為一個小工程,然后在組成一個一個完整的項目。
如何組件化
- 使用cocoapods管理組件化開發(fā)
-
podspec:描述自己組件工程的代碼目錄和資源目錄在哪,還有自己組件工程所依賴其他框架,到時候就會根據(jù)podspec的指引去引入自己的倉庫代碼.- 命令:
pod spec create spec文件名
// 創(chuàng)建pod索引庫,固定寫法,并且定義索引庫的名字為s,后續(xù)通過s,就能拿到索引庫 Pod::Spec.new do |s| // 設(shè)置名稱 s.name = "HttpManager" // 設(shè)置版本號 s.version = "0.0.1" // 設(shè)置摘要 s.summary = "A short description of HttpManager." // 設(shè)置詳情 s.description = "Good" // 設(shè)置倉庫主頁 s.homepage = "http://EXAMPLE/HttpManager" // 設(shè)置許可證 s.license = "MIT" // 設(shè)置作者 s.author = { "iThinkerYZ" => "690423479@qq.com" } // 設(shè)置倉庫源,表示在哪可以找到組件工程 s.source = { :git => "", :tag => "#{s.version}" } // 設(shè)置 源文件路徑 => 不是整個工程的文件,而是自己封裝的代碼,以后別的工程引入,就會引入這里的代碼。 s.source_files = "HttpManager/Classes/**/*.{h,m}" // s.dependency = '' 組件工程依賴哪些第三方框架 // s.frameworks = 'UIKit', 'MapKit' 組件工程依賴哪些原生框架 // s.resource_bundles = {} 組件工程圖片資源 endpodspec文件注意點:
s.source_files僅僅描述組件代碼就好,不要描述整個工程名文件,會把所有文件集成上去,錯誤寫法:s.source_files = "HttpManager"-
**:表示所有文件:因為*表示通配符,可有可無.-
s.source_files = "HttpManager/Classes/**/*.{h,m}",表示組件代碼在podspec目錄下HttpManager/Classes中的所有文件,默認(rèn)會自動追蹤到到podspec文件的目錄路徑下,因為當(dāng)前處于podspec文件中,處于哪個文件,就自動追蹤哪個文件。 -
HttpManager/Classes/a.h匹配到的應(yīng)該是HttpManager/Classes/**,表示HttpManager/Classes/a.h后沒有東西,就不會在找,直接匹配到。
-
podspec文件注意點:
s.description:不能為空podspec文件注意點:
s.license:不能亂填,必須是有這樣的協(xié)議,比如(MIT)
- 命令:
-
podFile文件:指定主工程加載哪些組件庫,里面描述好組件庫對應(yīng)的podspec文件在哪,就知道去哪加載組件代碼。- 創(chuàng)建命令:
pod init - cocoapods可以加載遠(yuǎn)程倉庫也可以加載本地倉庫,一般加載遠(yuǎn)程倉庫.
- 如何加載本地倉庫代碼?
本地倉庫代碼搞一個podspec文件描述去哪加載組件代碼
誰需要引入本地倉庫代碼,就創(chuàng)建Podfile
-
Podfile:指定podspec文件在哪
pod 'HttpManager' , :path => '../HttpManager'-
../HttpManager:回到Podfile上一級目錄,進(jìn)入HttpManager就能找到podspec - Podfile在哪,就自動跟隨在哪級目錄
-
- 創(chuàng)建命令:
-


- 創(chuàng)建組件工程
- 方式一: 直接創(chuàng)建工程,把組件代碼放在Class中,只要spec描述好,就會自動加載Class中組件代碼
- 生成spec描述文件,指定加載組件工程的組件代碼在哪。
- 方式二: 使用cocoapods命令:
pod lib create 組件代碼名稱-
這個命令會自動生成一套組件代碼工程測試代碼,并且有Git管理
Snip20170223_16.png - 還會生成podspec,描述好組件代碼在哪就好.
- 如何使用自動生成的組件工程代碼?
需要把自己的組件代碼放在Class中對應(yīng)文件,還不夠,發(fā)現(xiàn)根本沒法引入組件代碼.h文件.
需要重新pod install,因為不重新pod install,Example工程根本不知道Pod更新了,pod install的作用:重新讓pod庫與所依賴的工程文件產(chǎn)生關(guān)聯(lián)。
-
- 方式一: 直接創(chuàng)建工程,把組件代碼放在Class中,只要spec描述好,就會自動加載Class中組件代碼
Cocopods原理
- 根據(jù)Podfile描述,找到對應(yīng)代碼庫的描述文件podspec
- podspec中描述,去哪(s.source)才能找到代碼庫,并且找到之后,需要拷貝哪些文件(s.source_files)到自己的工程中。
如何組件化(公共遠(yuǎn)程倉庫)
- 問題:以上的代碼倉庫都在本地,沒有遠(yuǎn)程管理,也就意味著只能自己使用,別人不能使用,如果想把自己的代碼倉庫開源,讓所有的開發(fā)人員都可以弄,應(yīng)該怎么做?
- 把自己的倉庫代碼推送到cocoapods中索引庫中就好了。
- 原理:我們之前安裝cocoapods時,有一步驟pod setup,這一步其實就是去下載cocoapods中公共的所有代碼倉庫索引,保存到本地
- 通過pod repo 指令就能查看cocoapods的索引庫

- 如何把自己代碼倉庫上傳到cocoapods上
給自己代碼倉庫,創(chuàng)建podspec,其實本質(zhì)是把自己代碼倉庫的podspec索引文件上傳上去就好了,可以直接使用pod lib create命令
-
自己倉庫代碼遠(yuǎn)程托管,并且公開,這樣別人才能獲取到你的倉庫代碼
- 注意:遠(yuǎn)程倉庫不需要創(chuàng)建
gitignore文件,因為pod lib創(chuàng)建了 - 提交自己倉庫代碼到遠(yuǎn)程倉庫
-
git status: 查看狀態(tài),如果有不想要的文件,可以用gitignore忽略掉 - 提交到本地緩存區(qū) `git add .``
- 提交到本地倉庫
git commit -m '' - 查看遠(yuǎn)程倉庫地址
git remote(查看有沒有遠(yuǎn)程地址) - 綁定遠(yuǎn)程地址
git remote add origin遠(yuǎn)程倉庫地址 - 推送自己代碼到遠(yuǎn)程倉庫
git push origin master
- 注意:遠(yuǎn)程倉庫不需要創(chuàng)建
-
給自己倉庫綁定Tag,因為cocoapods是根據(jù)代碼倉庫的Tag,去下載對應(yīng)Tag的遠(yuǎn)程代碼庫的。
- 給自己當(dāng)前倉庫版本添加Tag標(biāo)簽,
git tag -a 0.0.1 -m '0.0.1',僅僅是本地 - 把本地標(biāo)簽推送到遠(yuǎn)程服務(wù)器,
git push --tags
- 給自己當(dāng)前倉庫版本添加Tag標(biāo)簽,
-
注冊trunk
- 注冊trunk,不是任何人都能推送,因為cocoapods依賴trunk服務(wù)器管理,所以需要通過trunk推送自己的podspec(cocoapods官網(wǎng))
- 命令:
pod trunk register EMAIL [NAME] - pod trunk register 58999050@qq.com yuanzheng
- 驗證成功后,點擊郵箱就好了,打開會有點慢.
-
推送自己的podspec到cocoapods的索引庫
pod trunk push HttpManager.podspec --allow-warnings- 注意:必須cd 進(jìn)入到podspec目錄下,才能執(zhí)行這個代碼
- 注意:podspec文件中的
s.version版本號要跟最新Tag一致 - 注意:podspec文件中的
s.source倉庫地址也不能寫錯
-
測試能否索引到
- pod search 自己倉庫
- 發(fā)現(xiàn)索引不到,其實已經(jīng)上傳到cocoapods上了,只不過需要重新更新索引文件
- 怎么更新pod索引文件?
- 原理:pod setup成功后會生成~/Library/Caches/CocoaPods/
search_index.json文件 - 把
search_index.json文件文件刪除,重新執(zhí)行pod search,就會重新更新索引.
- 原理:pod setup成功后會生成~/Library/Caches/CocoaPods/
如何組件化(私有遠(yuǎn)程倉庫)
- 問題:有些公司核心的代碼不想開源,就不能放在cocoapods公共的索引庫中,也不能放在本地,因為以后需要多人開發(fā),cocoapods支持創(chuàng)建自己的私有索引庫,只需要把自己的代碼倉庫放在自己的私有索引庫就好了.
- 如何創(chuàng)建私有遠(yuǎn)程倉庫索引庫
搞個私有遠(yuǎn)程索引庫托管平臺
本地添加私有遠(yuǎn)程索引庫:
pod repo add XMGSpec https://git.coding.net/iThinkerYZ520/XMGSpec.git,后面是索引庫遠(yuǎn)程地址把自己私有庫的索引添加到自己私有庫種:
pod repo push XMGSpec XMGLib.podspec --allow-warnings,本地索引庫就會有自己的私有庫,并且遠(yuǎn)程也會有,pod repo push會幫我們推送到遠(yuǎn)程索引庫.
- 怎么使用自己的私有索引倉庫
pod search 搜索自己庫描述
pod install,發(fā)現(xiàn)找不到,因為默認(rèn)是去共有的索引庫查找
-
需要在Podfile文件頂部添加一個源,表示去哪個地方查找。
source 'https://git.coding.net/iThinkerYZ520/XMGSpec.git'
-
但是有問題,如果以后要添加公有的索引庫,比如AFN,就找不到了
- 因此還需要在添加一個公有索引庫源
source 'https://github.com/CocoaPods/Specs.git'
# 表示先去找私有,在找公有 source 'https://git.coding.net/iThinkerYZ520/XMGSpec.git' source 'https://github.com/CocoaPods/Specs.git' target '測試私有索引庫' do pod 'XMGLib' pod 'AFNetworking', '~> 3.1.0' end
組件化升級
- 以后各個組件要不斷更新完善,怎么管理。
- 只需要把最新的版本代碼綁定tag,更新Podspec文件,重新上傳到版本索引庫就好了
pod repo push XMGSpec XMGLib.podspec --allow-warnings
- 工程文件在使用的時候,使用pod update 就能加載最新版本組件代碼.
pod update --no-repo-update
- 只需要把最新的版本代碼綁定tag,更新Podspec文件,重新上傳到版本索引庫就好了
組件化資源
- 組件工程資源放哪?
- 使用
pod lib create創(chuàng)建的組件工程,有個Assets文件夾,把圖片放這 - 然后podspec指定資源文件路徑
s.resource_bundles - 倉庫代碼重新pod install就好了,會自動生成bundle文件,圖片就保存到這。
- 使用


- 因為podspec指定的圖片資源就是這個路徑,就會自動查找這個路徑.
- 如何使用組件代碼的資源?
- 之前獲取圖片都是在主bundle中:
NSBundle mainBundle,但是組件資源代碼,不是在主bundle中,是在自己框架的bundle中 - 獲取自己bundle,[NSBundle bundleForClass:self],self:表示當(dāng)前類,當(dāng)前方法獲取當(dāng)前類所在的bundle
-
因為當(dāng)前方法在自己框架中,就能獲取自己的bundle
Snip20170213_5.png
-
- 之前獲取圖片都是在主bundle中:
```
NSBundle *selfBundle = [NSBundle bundleForClass:self];
NSLog(@"%@",selfBundle);
// 獲取bundle還不夠,圖片在bundle的XMGLib.bundle文件中
// 注意圖片要全名
NSString *path = [selfBundle pathForResource:@"XMGLib.bundle/圖片@2x.png" ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:path];
```
- 注意:可能項目會遇見如圖bug

-
原因:導(dǎo)入的組件代碼有xib,但是找不到xib運行,因為xib屬于資源,不能當(dāng)成代碼導(dǎo)入,也就是需要用resource_bundles導(dǎo)入。
Snip20170223_20.png - 運行:找不到xib,因為xib被存放到資源的bundle中,因此需要添加前綴
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
// 獲取當(dāng)前bundle名稱
NSString *bundleName = bundle.infoDictionary[@"CFBundleName"];
bundleName = [NSString stringWithFormat:@"%@.bundle",bundleName];
// xib名稱需要拼接Bundle名稱,否則找不到xib
NSString *nibName = [NSString stringWithFormat:@"%@/XMGHomeRecommendCell",bundleName];
[self.tableView registerNib:[UINib nibWithNibName:nibName bundle:[NSBundle bundleForClass:[self class]]] forCellReuseIdentifier:ID];
- 友情提醒:如果組件中使用了資源,一定要把組件生成framework,不能生成.a,否則資源拿不到.
- podfile文件中添加描述
use_frameworks!

- 使用use_frameworks和不使用use_frameworks的區(qū)別


框架依賴
- podspec文件中描述:
s.dependency 'AFNetworking'- 在使用組件的時候就會自動導(dǎo)入第三方庫
如何組件化(劃分子組件)
- 隨著組件不斷擴(kuò)大,業(yè)務(wù)也會越來越多,如果不劃分子組件,可能我們的工程有時候并不需要導(dǎo)入那么多的業(yè)務(wù),也會一起導(dǎo)入到自己工程,造成自己工程不必要的代碼太多,所以在大公司一般都會為自己的工程瘦身,就是減少不必要的代碼。
- 一個好的組件,要劃分好子組件,別人在加載你的組件的時候,就可以根據(jù)自己的需求,加載對應(yīng)的組件代碼
- 以SDWebImage為例,內(nèi)部有四個子組件,有一個組件專門用來加載gif圖片.

- SDWebImage的podspec文件,描述了子組件,格式固定
- 注意:自己子組件如果依賴其他子組件,一定要寫依賴子組件,否則子組件不能用,下面就依賴Core子組件
注意:GIF 和 gif不能同名,ruby語法,GIF是外界搜索子組件名稱,gif是變量,用于后面訪問如果使用了subspec,別人引入你的框架,代碼也會按照subspec劃分文件夾結(jié)構(gòu),否則所有代碼都在一起,不管你之前是否劃分好,比如SDWebImage就會有GIF文件夾- 還有如果使用subspec,就不需要描述整個文件夾路徑,會造成subspec劃分的文件夾沒有代碼,用
#注釋掉之前的描述。
- 注意:自己子組件如果依賴其他子組件,一定要寫依賴子組件,否則子組件不能用,下面就依賴Core子組件

s.subspec 'GIF' do |gif|
gif.ios.deployment_target = '7.0'
gif.source_files = 'SDWebImage/FLAnimatedImage/*.{h,m}'
// 設(shè)置依賴,依賴自己組件的子組件Core
gif.dependency 'SDWebImage/Core'
gif.dependency 'FLAnimatedImage', '~> 1.0'
gif.xcconfig = {
'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/FLAnimatedImage/FLAnimatedImage'
}
end
- 使用subspec和不使用subpec區(qū)別

-
如何加載子組件
- podfile文件描述
// 只會引入GIF組件 pod 'SDWebImage/GIF'


