1、介绍
- 组件化将项目拆分成独立组件,使用特定通讯方式来解耦
1.1、组件化优点
- 解耦
- 模块复用
- 单元测试
- 功能拆分给不同组去开发
1.2、组件化分层
- 一般项目的组件化分为
业务层、通用层、基础层 - 划分层时可能会用到UML整理关系
- 实现关系:
虚线 + 空心箭头 - 依赖关系:
虚线 + 实心箭头 - 继承关系:
实线 + 空心箭头 - 关联关系:
实线 + 实心箭头- 组合关系:
实心菱形表示,由关联主体指向被关联的部分,菱形处于关联主体的位置;这种关联作用是一种强拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样(类似鸟与翅膀的关系,没有翅膀的鸟就不是鸟) - 聚合关系:
空心菱形表示,由关联主体指向被关联的部分,菱形处于关联主体的位置;这种关联作用是一种弱拥有关系,体现的是A对象可以包含B对象,但是B对象不是A对象的一部分(类似班级和学生,学生转学了这个班级照旧)
- 组合关系:
- 实现关系:
2、CocoaPods
- CocoaPods 是第三方依赖库的管理工具,组件化可以将功能模块做成库,再通过CocoaPods引入进工程
2.1、CocoaPods本地索引库
-
导入一个三方框架时,会在 本地CocoaPods的索引库 中进行查找,路径:
.cocoapods隐藏文件夹-->repos文件夹 --> 仓库名 -->Specs-->MD5加密文件名分类--> 三方库 -
三方库名称经过 MD5加密 会得到一串数,其中 前3个字符 就是所属的数字文件夹;试一下 YYKit:
再试一个AFNetworking
-
在
.podspec.json文件中,找到该框架在远程仓库的下载地址,从远程仓库将其导入
-
CocoaPods流程图
2.2、创建组件
- 使用 CocoaPods,可分为 本地 和 远程 两种方式搭建组件化工程
- 本地:通过项目中创建模块,利用 CocoaPods 的
workspec进行本地管理,不需要将项目上传 git,而是在项目的Podfile中指定目录 - 远程:利用 CocoaPods 进行模块的远程管理,需要将项目上传 git;公司项目一般使用
私有库
- 本地:通过项目中创建模块,利用 CocoaPods 的
搭建本地组件化工程
-
cd 到想创建模块的文件夹下
pod lib create LZHomeModule ------------------------- //对模块进行以下配置: //工程类型 What platform do you want to use?? [ iOS / macOS ] > iOS //开发语言 What language do you want to use?? [ Swift / ObjC ] > objc //创建App测试项目 Would you like to include a demo application with your library? [ Yes / No ] > yes //提供frameworks的测试 Which testing frameworks will you use? [ Specta / Kiwi / None ] > none //提供测试文件 Would you like to do view based testing? [ Yes / No ] > no //设置前缀 What is your class prefix? > LZ- 创建时可能遇到报错
fatal: unable to access 'github.com/CocoaPods/p…': HTTP/2 stream 1 was not closed cleanly before end of the underlying stream
- 解决方法:提示默认 通信协议 出错,更改默认通信协议:
git config --global http.version HTTP/1.1
- 创建时可能遇到报错
-
直接生成带
workspace的工程 -
组件库功能代码 文件夹中路径是创建的 同名文件夹(LZHomeModule),工程中是
Pods中的内容,Example 中的是测试用例 -
替换 Replace.m 文件,实现业务代码
-
在测试用例
Example目录下,执行pod install,将组件集成进来-
这里可能会报错:
project 'path/to/Project.xcodeproj',原因是 没有指定工程目录 -
解决方法:在 podfile 文件中指定下工程目录就行了
project '项目名.xcodeproj'
-
Xcode会有反应,问你文件在外部被修改,是 Read From Disk 还是 Keep Xcode Version
-
-
install成功后,工程中 Pods文件夹 中就能看到组件导入成功
2.3、组件与三方库的依赖
2.3.1、三方与本地组件的依赖
-
配置 组件依赖库
.podspec文件:Pods --> Development Pods --> 组件名 --> Pod --> LZHomeModule.podspec -
在文件后边追加:
s.dependency '库名' -
在测试工程 Example 目录下,执行
pod install,解决三方框架的依赖问题
2.3.2、组件与下层本地组件的依赖
- 除了依赖三方框架,我们的组件也会对下层的本地组件进行依赖,比如:分类 和 宏定义 等
-
我们重复上边的步骤,再创建一个 LZCommonModule 基础组件,添加一个 Macro 文件,并引入几个三方库
-
我们想要在 LZHomeModule 中使用 LZCommonModule 中的 Macro 文件内容和三方库的话,要在
.podspec文件中添加对 基础组件 的引用与 头文件导入
prefix_header_contents:类似于pch文件,多个引用用逗号隔开
-
但是光在 .podspec 文件中添加依赖还是不够的,因为本地组件还需要在 Podfile文件 中指定 组件路径(如果在云端则不报错),并在Example中执行 pod install
-
依赖完成后即可在 LZHomeModule 组件中使用 LZCommonModule 中的 宏、分类与三方库 等(两个组件同时依赖同一个三方库,但是版本号不同的话会报错)
2.4、加载资源文件
2.4.1、加载图片资源
- 与正常项目不同,组件资源不能使用
imageNamed方法加载图片(该方法在主Bundle中加载图片),图片要放入Images.xcassets中
-
组件内的图片资源存储在
组件 / Assets目录下,需要我们创建Images.xcassets文件夹 -
在 .podspec 文件中配置图片资源路径,并在 测试用例Example 中执行 pod install
-
将图片添加进 Images.xcassets 中
-
在测试用例中通过指定
Bundle访问组件内的图片资源@implementation LZViewController - (void)viewDidLoad { [super viewDidLoad]; UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)]; // Bundle中读取图片 NSString *bundlePath = [[NSBundle bundleForClass:[HomeXViewController class]].resourcePath stringByAppendingPathComponent:@"/LZHomeModule.bundle"]; NSBundle *resoure_bundle = [NSBundle bundleWithPath:bundlePath]; imageView.image = [UIImage imageNamed:@"xuetang" inBundle:resoure_bundle compatibleWithTraitCollection:nil]; [self.view addSubview:imageView]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { HomeXViewController *homeVC = [HomeXViewController new]; [self presentViewController:homeVC animated:YES completion:nil]; } @end- HomeXViewController 是组件中的页面,用于获取
Pods 的bundle路径,因为 不能用 [NSBundle mainBundle] 的方式获取组件的Bundle路径
@implementation HomeXViewController - (void)viewDidLoad { [super viewDidLoad]; // 使用 CommonMacro.h 文件中的宏 WS(self); // 使用 SDWebImage UIImageView *imgView = [[UIImageView alloc] init]; [imgView sd_setImageWithURL:[NSURL URLWithString:@"图片链接"]]; UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 150, 150)]; imageView.backgroundColor = [UIColor redColor]; // 组件页面加载组件图片 NSString *bundlePath = [[NSBundle bundleForClass:[self class]].resourcePath stringByAppendingPathComponent:@"/LZHomeModule.bundle"]; NSBundle *resoure_bundle = [NSBundle bundleWithPath:bundlePath]; imageView.image = [UIImage imageNamed:@"xueyang" inBundle:resoure_bundle compatibleWithTraitCollection:nil]; [self.view addSubview:imageView]; } @end - HomeXViewController 是组件中的页面,用于获取
2.4.2、加载Json文件
- 资源路径也是在 组件 / Assets 目录下,操作基本一致,只是不用使用 Images.xcassets
- 调用方法也与图片类似
NSString *bundlePath = [[NSBundle bundleForClass:[self class]].resourcePath stringByAppendingPathComponent:@"/LZHomeModule.bundle"]; NSString *path = [[NSBundle bundleWithPath:bundlePath] pathForResource:[NSString stringWithFormat:@"JsonFile_%@", num] ofType:@"json"]; NSData *data = [NSData dataWithContentsOfFile:path];
2.4.3、加载xib文件
- 读取方式,指定Bundle(可以 将Home模块中有xib的cell集中到宏中)
// 将有xib的Cell集中到宏中 #define HomeTableViewCellIdentifiers @{@"homeTable": @"HomeXTableViewCell",\ @"homeTitleTable": @"HomeTitleTableViewCell",\ @"homeContentTable": @"HomeContentTableViewCell"} for (NSString *className in HomeTableViewCellIdentifiers.allValues) { NSString *bundlePath = [NSBundle bundleForClass:[self class]].resourcePath; [self.tableView registerNib:[UINib nibWithNibName:className bundle:[NSBundle bundleWithPath:bundlePath]] forCellReuseIdentifier:className]; }
2.5、远程私有组件
- 我们使用
gitee来搭建远程私有组件,需要两个东西,一个是私有库索引,另一个是组件库;因此我们在 gitee 上 创建2个仓库
2.5.1、创建私有库索引
-
我们将上边的 LZCommonModule 做成远程组件,所以私有库索引我们命名为 LZCommonModuleSpec
-
私有库索引 创建完成,与创建一个项目是一样的
2.5.2、本地添加私有库索引
-
查看本地索引库
// 终端命令,查看 本地的公有索引库 % pod repo -
添加私有库索引
// 终端命令,添加索引库 % pod repo add 索引库名 索引库地址 // 如果需要删除索引库 % pod repo remove 索引库名- 添加后再执行 pod repo 命令,在本地索引库中能看到
-
创建远程组件库
2.5.3、创建本地库
-
执行 2.2 中的操作,搭建本地组件化工程;执行 2.3 中操作添加依赖;执行 2.4 中操作加载资源文件
-
修改.podspec文件s.homepage:组件库 主页地址s.source:组件库 链接地址
2.5.4、关联组件代码
-
将组件代码关联到仓库
% cd 组件文件夹(文件夹下内容都会被上传到云端) % git add . % git commit -m "关联组件代码" % git remote add origin 组件链接地址 % git push -f origin master组件代码和远程仓库关联完成
-
打tag并上传到git
% git tag -m 'tag描述' '0.1.2' % git push --tags注: 所打的 tag 要和
.podspec中的s.version保持一致,否则后面不能通过远程验证
2.5.5、验证
-
本地验证
% pod lib lint --private --allow-warnings- 验证成功:CCBasicComponents passed validation
-
远程验证
% pod spec lint --private --allow-warnings注:在远程验证会验证远程tag下面的tag的编号和所有的组件,tag必须要相同才能通过;另外组件更新后需要重新传到远程tag下,否则也会验证失败
2.5.6、提交.podspec文件
-
终端执行指令
% pod repo push 远程库索引Spec名 本地组件.podspec文件 --allow-warnings -
此时码云的私有索引库可以看到组件已经上传成功
-
验证组件
% pod search 组件名- 此时组件已可使用
2.5.7、更新组件
-
对组件代码进行修改
-
cd 进组件文件夹,执行下面命令,更新远程仓库代码:
% git add . % git commit -m "修改描述" % git push origin master -
更新tag版本
- 修改 podspec文件,将 s.version 改为最新
- 执行命令:
% git tag -a '0.2.0' -m '新tag:0.2.0' % git push --tags - 此时远程组件库会多出一个 tag
-
重复 2.5.5 的操作进行 本地与远程验证
-
重复 2.5.6 的操作进行 更新索引库
% pod repo push LZComponentSpec LZCommonModule.podspec
2.6、使用远程组件
- 与本地组件相比,将
本地路径换成远程链接(.podspec文件 中内容还是不能减少,否则头文件等会找不到)--> pod install
注意:
- 用
github关联仓库 和 使用组件 时,要使用access tokens
- 关联仓库
#关联仓库 # % git remote add origin https://access tokens远程链接 % git remote add origin https://ghp_xxxxx@github.com/Wind/LZCommonModule.git - 使用组件
platform :ios, '9.0' target 'LZHomeModule_Example' do # use_frameworks! pod 'LZCommonModule', :git => 'https://ghp_xxxxx@github.com/Wind/LZCommonModule.git' end
-
如果本地代码已经关联其他仓库,再次关联时会报错:
remote origin already exists#关联仓库 % git remote add origin https://ghp_xxxxx@github.com/Wind/LZCommonModule.git ------------------------- fatal: remote origin already exists此时需要解除之前的关联
% git remote rm origin -
其余配置
- Git 配置多个
SSH-Key:gitee.com/help/articl… - Git 常用命令:gitee.com/all-about-g…
- CocoaPods 指南:guides.cocoapods.org/
3、通讯方式
- 主要使用
target-action、URL路由、protocol三种方式,可以参考 iOS常用知识 --> 10、组件化- protocol 方案的代表框架:BeeHive