前言:
一直一来,总是听到组件化这个词,项目也进行了组件化. 现在来写写关于组件化的一些实践吧,理论以及组件化的框架大佬都讲的很好了.
简介:
组件化顾名思义,就是把app 里的模块相互分离开来,既可以进行业务组件的分离,也可以对一些常用的基础组件如网络请求的封装,图片展示等的封装. 举个支付宝的例子, 支付宝这个app 里,集成了蚂蚁财富,口碑.组件化要做的就是支付宝作为主app,运用组件化方法,可以让蚂蚁财富,口碑很便捷的继承进支付宝,蚂蚁财富,口碑还可以作为单独的app运行. 通过github 或者自己建的代码仓库,让口碑,蚂蚁财富团队开发的项目更新,提交代码,支付宝可以通过cocoapods 继承进支付宝.这样是不是管理起来很方便.
优点:
降低耦合度,小模块可以单独开发,单独测,适于多人团队协作开发.
缺点: 增加项目复杂度,还会有版本同步,以及,项目过大后,实施组件化费时费力等,
所以建议项目快速迭代一个版本以后,即有计划的引入组件化.省掉了后期实施组件化的大麻烦.
组件化我这边是用的cocoapods创建.
1 我们先建立一个工程吧(你可以把这个工程当做你要组件化的项目,毕竟大部分项目一开始是没有进行组件化的,都是做了一段时间之后才着手组件化)
2 假定我们要分离的第一个模块叫做 口碑 我们起名字叫 WordOfMouth
打开终端,进入到我们要放置口碑模块的位置. 终端执行 pod lib create WordOfMouth如下:
Cloning `https://github.com/CocoaPods/pod-template.git` into `WordOfMouth`.
Configuring WordOfMouth template.
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
What platform do you want to use?? [ iOS / macOS ]
> iOS
What language do you want to use?? [ Swift / ObjC ]
> Swift
Would you like to include a demo application with your library? [ Yes / No ]
> Yes
选择No的话子工程无法单独运行调用代码.
Which testing frameworks will you use? [ Quick / None ]
> None
Would you like to do view based testing? [ Yes / No ]
> No
得到的工程如下:
模块代码自动集成到了 Development Pods.
需要集成的子模块的项目实际是存放在 WordOfMouth/WordOfMouth/Classes 这个文件夹下
接下来我们随意新建个vc来替代ReplaceMe.swift.
我新建了一个 WordOfMouthMainViewController 的vc, 放在classess 里然后
在终端进入到 WordOfMouth/Example 文件夹下 来一下我们熟悉的 pod install
有了哈,如图:
我现在WordOfMouth这个项目里调用一下 WordOfMouthMainViewController.
1 ViewController 里代码如下
import UIKit
import WordOfMouth
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "口碑模块"
view.addSubview(wordOfMouthBtn)
// Do any additional setup after loading the view, typically from a nib.
}
private lazy var wordOfMouthBtn: UIButton = {
let btn = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 30))
btn.setTitle("口碑", for: .normal)
btn.setTitleColor(UIColor.black, for: .normal)
btn.addTarget(self, action: #selector(goWordOfMouth), for: .touchUpInside)
return btn
}()
@objc func goWordOfMouth() {
print("跳转到口碑页面")
// 实际组件化的时候,这样调用肯定是不合适的. 方便实践组件化,暂时先这么写.
let vc = WordOfMouthMainViewController()
self.navigationController?.pushViewController(vc, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
WordOfMouthMainViewController 代码如下
import UIKit
public class WordOfMouthMainViewController: UIViewController {
override public func viewDidLoad() {
super.viewDidLoad()
self.title = "口碑"
self.view.backgroundColor = UIColor.white
// Do any additional setup after loading the view.
}
}
此处需要注意两点
1 模块外调用,需用public修饰切记切记. 不熟悉的朋友可以着重 了解下private,fileprivate,internal,public 和 open.
2 WordOfMouthMainViewController 改变之后pod install 发觉没有改变,需cmd+ shift + k clean 一下,或者Podfile 加如下一句话
install! 'cocoapods', :disable_input_output_paths => true
接下来我们在主工程里引用口碑这个模块.
终端进入到 主工程目录下 pod init 初始化 Podfile 文件.
然后 vim Podfile 文件里引用 WordOfMouth
pod 'WordOfMouth', :path => '../workspace/WordOfMouth'
Podfile 文件如下
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
install! 'cocoapods', :disable_input_output_paths => true
target 'ComponentDemo' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for ComponentDemo
pod 'WordOfMouth', :path => '../workspace/WordOfMouth'
target 'ComponentDemoTests' do
inherit! :search_paths
# Pods for testing
end
target 'ComponentDemoUITests' do
inherit! :search_paths
# Pods for testing
end
end
接下来我们在 新建一个组件名字叫做蚂蚁财富 用于代码放在git上然后用cocopods调用.
蚂蚁财富的模块 创建跟上面一样.
然后将创建好的模块上传到github. 或者公司自己的gitlab. 等等,这里我用一个国内的git 码云.
在账号里新建一个仓库.
在这里我把这个仓库设置为公开,方便大家调用.
然后 可以git clone git@gitee.com:godlovexq/AntWealth.git
把原来创建的工程AntWealth下的文件放到git 的AntWealth目录下.
然后cd AntWealth
git add .
git commit -m "code add"
git push
好了码云上已经有代码了
然后 主工程 的Podfie 引用
pod 'AntWealth', :git => 'git@gitee.com:godlovexq/AntWealth.git'
或者引用某个tag
pod 'AntWealth', :git => 'git@gitee.com:godlovexq/AntWealth.git', :tag => '0.1.0'
指定版本需 有对应的tags
git tag 'v0.1.0’ -m ‘ 0.1.0备份'
git push --tags
如上引用 只需要关注
AntWealth.podspec 里的s.version版本号就好,下面那一堆暂时不用管,
或者直接如引用三方库那样.
pod 'AntWealth', '~> 0.1.0' (这个方式我这边暂时没弄通,用的上面的办法)
这时候需要加如下两句话,告诉pod 从这两个地址搜索
source 'https://gitee.com/godlovexq/AntWealth.git'
source 'https://github.com/CocoaPods/Specs.git'
比较麻烦,不建议.
另外,资讯了一些大佬,通俗的做法都是把代码直接上传到公司服务器,需要的时候,本地引用省去了代码版本控制的麻烦.(通过公司git 控制版本) 也是一个不错的选择.以上,完.
建议此大佬出的组件化方案
https://casatwy.com/iOS-Modulization.html
具体实践:
1 在组件里写好中间件. 代码如下
AntWealth+CTExt.swift:
//
// AntWealth+CTExt.swift
// AntWealth_Example
//
// Created by qiangfei dong on 2020/6/11.
// Copyright © 2020 CocoaPods. All rights reserved.
//
import CTMediator
public extension CTMediator {
@objc func AntWealth_AntWealthController(callback:@escaping (String) -> Void,name:String) -> UIViewController? {
let params = [
"callback":callback,
kCTMediatorParamsKeySwiftTargetModuleName:"AntWealth",
"name": name
] as [AnyHashable : Any]
print(name)
if let viewController = self.performTarget("AntWealth", action: "AntWealthViewController", params: params, shouldCacheTarget: false) as? UIViewController {
return viewController
}
return nil
}
}
Target_AntWealth.swift:
import UIKit
@objc class Target_AntWealth: NSObject {
@objc func Action_AntWealthViewController(_ params: [AnyHashable: Any]) ->
UIViewController {
if let callback = params["callback"] as? (String) -> Void {
callback("success")
}
print("这里可以穿参数")
print(params)
let vc = AntWealthViewController()
if let name = params["name"] {
vc.name = name as! String
}
return vc
}
}
Podfile 引用 pod 'CTMediator'
AntWealth.podspec 里 依赖 s.dependency 'CTMediator'
主工程里声明 调用:
import UIKit
import AntWealth
import CTMediator
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "蚂蚁财富模块"
// Do any additional setup after loading the view, typically from a nib.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//let vc = AntWealthViewController()
//self.navigationController?.pushViewController(vc, animated: true)
let vc = CTMediator.sharedInstance().AntWealth_AntWealthController(callback: { (str) in
print(str)
}, name: "传参")
if let tempVC = vc {
self.navigationController?.pushViewController(tempVC, animated: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}