本文主要講解組件化的兩種方案
組件化
組件化其實就是將模塊單獨抽離、分層
,并指定模塊間的通訊
方式,從而實現解耦
的一種方式,主要運用與團隊開發
為什么需要組件化?
主要有以下四個原因
1、模塊間解耦
2、模塊重用
3、提高團隊協作開發效率
4、單元測試
當項目因為各種需求,越來越來時,如果此時的各個模塊之間是互相調用,即你中有我,我中有你
這種情況時,會造成高耦合
的情況。一旦我們需要對某一塊代碼進行修改
時,就會牽一發而動全身
,導致項目難以維護
其問題主要體現在以下幾個方面:
1、修改某個功能時,同時需要修改其他模塊的代碼,因為在其他模塊中有該模塊的引用。可以理解為
高耦合導致代碼修改困難
2、模塊對外接口不明確,甚至暴露了本不該暴露的私有接口,修改時費時費力。可以理解為
接口不固定導致的接口混亂
3、高耦合代碼產生的后果就是會影響團隊其他成員的開發,產生
代碼沖突
4、當模塊需要重用到其他項目時,
難以單獨抽離
5、模塊間耦合的忌口導致接口和依賴關系混亂,
無法進行單元測試
所以為了解決以上問題,我們需要采用更規范的方式來降低模塊
間的耦合度
,這就是組件化
,也可以理解為模塊化
但是,這里還需要說明一點,因為組件化也是需要一定成本的,需要花費時間設計接口、分離代碼等,所以并不是所有的項目都需要組件化。如果你的項目有以下這些特征就不需要組件化
:
1、項目較小,模塊間交互簡單,耦合少
2、項目沒有被多個外部模塊引用,只是一個單獨的小模塊
3、模塊不需要重用,代碼也很少被修改
4、團隊規模很小
5、不需要編寫單元測試
如果你的有以下特性,說明你就必須要考慮進行組件化
了:
1、模塊邏輯復雜,多個模塊之間頻繁互相引用
2、項目規模逐漸變大,修改代碼變的越來越困難(這里可以理解為:修改一處代碼,需要同時修改其他多個地方)
3、團隊人數變多,提交的代碼經常和其他成員沖突
4、項目編譯耗時較大
5、模塊的單元測試經常由于其他模塊的修改而失敗
組件化方案
組件化方案的8條指標
一個項目經過組件化后如何來評判,主要有以下幾個標準
:
- 1、模塊之間沒有耦合,模塊內部的修改不會應該其他模塊
- 2、模塊可以單獨編譯
- 3、模塊間數據傳遞明確
- 4、模塊可以隨時被另一個提供了相同功能的模塊替換
- 5、模塊對外接口清晰且易維護
- 6、當模塊接口改變時,此模塊的外部代碼能夠被高效重構
- 7、盡量用最少的修改和代碼,讓現有的項目實現模塊化
- 8、支持OC和Swift,以及混編
前4條主要用于衡量一個模塊是否真正解耦
,后4條主要用于衡量在項目中實踐中的易用程度
組件化原則
一個項目主要分為3層:業務層
、通用層
以及基礎層
,在進行組件化時,有以下幾點說明
只能上層對依賴,不能下層對上層的依賴,因為下層是對上層的抽象
項目公共代碼資源下沉
橫向的依賴盡量少有,最好下層至通用模塊,或者基礎模塊
組件化方案
常用的組件化方案主要有兩種:
本地組件化
:主要是通過在工程中創建library
,利用cocoapods
的workspec
進行本地管理,不需要將項目上傳git,而是直接在本項目中以framework的方法進行調用cocoapods組件化
:主要是利用cocoapods來進行模塊的遠程管理,需要將項目上傳git(需要注意:這里的組件化模塊分為公有庫
和私有庫
,對公司而言,一般是私有庫)
本地組件化
1、創建主工程
-
首先創建一個工程
工程結構 集成cocopods,進行本地管理:
$ cd 項目目錄
$ pod init
- 編輯podfile,并執行
pod install
2、創建組件
假設有以下幾個模塊:
-
主工程
:承載主要的表層業務代碼 -
Core
:獨立存在,應用加密、接口請求等敏感代碼 -
Base
:基類封裝,拓展,基本的數據處理 -
Service
:服務層,封裝業務工具類,例如網絡層服務、持久化服務等 -
Pods
:三方依賴
其中,各個模塊間的關系如下所示
下面,我們來進行模塊的創建,以Core
模塊為例:
-
選擇
new -> project -> iOS -> Framework
,新建一個模塊
創建library -
選擇正確的
Group
和WorkSpace
(這里需要注意一點:創建的library
最好放在主工程根目錄
下,否則后續podfile
執行pod install
時會報錯)
library與工程關聯 -
將創建的
library
的Build Settings -> Mach-O Type
修改為靜態庫Static Library
修改配置
3、主工程調用library
- 在CJLCore中新建一個文件,并添加如下代碼
//類需要聲明為public
public class CJLCoreSetting: NSObject {
//屬性需要聲明為public
public static let SCRET_KEY = "SCRET_KEY"
}
-
在
Build Phases -> Headers -> Public
中將新建的文件添加為public,這樣主工程才能訪問該文件
修改配置2 -
在主工程中,選擇
target -> Linked Frameworks Library
中添加CJLCore
,只需要build主工程,library能夠自動聯編
導入
4、使用
首先import CJLCore
,然后使用
這里需要注意的是,子library之間的互相調用,與主工程調用library類似,主需要添加依賴、暴露header即可
5、使用cocoapods管理三方依賴
假設我們需要在CJLCore
中封裝網絡層代碼,需要用到三方庫Alamofire
,在podfile中
platform :ios, '9.0'
inhibit_all_warnings!
use_frameworks!
#配置workspace路徑
workspace 'Modularization.xcworkspace'
################# 三方依賴
# 公有
def workspace_pods
pod 'SwiftyJSON'
end
# 主工程
def project_only_pods
pod 'SnapKit'
end
#網絡
def network_layer_pods
pod 'Alamofire'
end
################# 模塊
target 'CJLCore' do
#配置libray路徑
project 'CJLCore/CJLCore.xcodeproj'
workspace_pods
network_layer_pods
end
################# 主工程
target 'Modularization' do
workspace_pods
project_only_pods
network_layer_pods
target 'ModularizationTests' do
inherit! :search_paths
end
target 'ModularizationUITests' do
end
end
到此,一個本地組件化的模塊就配置完成了
cocoapods組件化
除了本地組件化,還可以使用cocoapods
,其原理如下圖所示
這里還是以本地組件化中的結構為例
1、創建私有倉庫
在github上創建一個MySpecs倉庫
具體步驟:登錄github-->點擊右上角“+”-->選擇 new repository-->輸入Repository name為MySpecs,選擇倉庫類型為 private,點擊Create repository。將私有倉庫添加至本地
~/.cocoapods/repos
目錄
pod repo add mySpecs https://github.com/xxx/MySpecs.git
2、創建pods 工程,即組件化工程
- 使用終端創建
CJLServices
模塊
pod lib create CJLServices
-
根據提示依次輸入:ios、swift、yes、none、no、CJL
創建-1 -
進入模塊的目錄,將文件拷貝至
CJLServices -> Classes
中
粘貼文件 -
執行
pod install
,會將Classes更新至pods中
執行結果
3、配置pods工程
修改模塊的配置文件,即CJLServices.podspec
- 如果需要依賴三方庫,需要配置
s.dependency
s.dependency 'AFNetworking'
- 如果模塊間需要相互引用,同樣需要配置
s.dependency
,以CJLBase
為例,需要引用CJLService
//********1、修改 podspec 文件
s.dependency 'CJLServices'
//********2、修改 podfile 文件
pod 'CJLServices', :path => '../../CJLServices'
- 如果需要加載資源,例如圖片、json、bundle文件等
- 1、在模塊的
Assets
文件夾 中添加資源文件 - 2、在
specs
里配置資源路徑(必須配置!!否則無法讀取資源) - 3、訪問時需要指定資源文件路徑
- 1、在模塊的
//*****1、修改 podspec 文件
s.resource_bundles = {
'CJLBase' => ['CJLBase/Assets/*']
}
//*****2、使用
let bundlePath: String = Bundle.init(for: dynamicClass.self).resourcePath! + "/CJLBase.bundle"
let bundle = Bundle(path: bundlePath)
if let path = bundle?.path(forResource: "mouse", ofType: "jpg"){
self.imgView.image = UIImage(contentsOfFile: path)
}
同理,模塊中的xib,json文件的獲取方式也是一樣的
4、提交至git
這里提交至git的模塊是pods工程才可以,以CJLBase
為例
需要在github上創建一個私有repository,命名為
CJLBase
執行以下終端命令
$git init
$git add .
$ git commit -am "第一次提交"
//即第一個步驟中創建的倉庫地址
$ git remote add origin https://github.com/xxx/CJLBase.git
$ git push origin master
//一定要有標簽,不然會有下面的警告
//podspec文件中獲取Git版本控制的項目需要tag號,
$ git tag -m "first release" "0.1.0"
$ git push --tags
5、驗證podspec文件
執行終端命令 pod spec lint
注意:
pod spec
相對于pod lib
會更為精確,
pod lib
相當于只驗證一個本地倉庫,pod spec
會同時驗證本地倉庫和遠程倉庫。
6、提交到私有倉庫
執行以下命令
pod repo push [本地Spec Repo名稱][podspec文件路徑]
//******舉例
$ pod repo push MySpecs CJLBase.podspec
提交成功后,可在本地倉庫中看到提交路徑MySpecs -> CJLBase
7、使用
- 新建一個工程,在項目的podfile里添加
#私有spec倉庫的地址,而不是某個pod倉庫的地址
source 'https://github.com/xxx/MySpecs'
pod 'CJLBase'
- 執行
pod install
即可