使用组件化的原因
在一个项目越来越大,开发人员越来越多的情况下,项目会遇到很多问题。
- 业务模块间划分不清晰,模块之间耦合度很大,非常难维护。
- 所有模块代码都编写在一个项目中,测试某个模块或功能,需要编译运行整个项目。
后来我们通过中间层进行组件化优化,只让其他模块对中间层产生耦合关系,中间层不对其他模块发生耦合。
进行组件化开发后,可以把每个组件当做一个独立的app,每个组件甚至可以采取不同的架构,例如分别使用MVVM、MVC、MVP等架构。
组件化的方案
组件化一般有三种方案,url-block方案、target-action方案和protocol-class方案,今天我们讲得是url-block方案。
下面是url-block的通信方式,通过在启动时注册组件提供的服务,把调用组件使用的url和组件提供的服务block对应起来,保存到内存中。在使用组件的服务时,通过url找到对应的block,然后获取服务。
上面是MGJRouter的组件通信流程,下面我们看一下他的实现的步骤:
- 安装
pod 'MGJRouter', '~>0.9.0'
- 最基本的使用
//注册url
[MGJRouter registerURLPattern:@"mgj://foo/bar" toHandler:^(NSDictionary *routerParameters) {
NSLog(@"routerParameterUserInfo:%@", routerParameters[MGJRouterParameterUserInfo]);
}];
//调用
[MGJRouter openURL:@"mgj://foo/bar"];
- 处理中文也没有问题
[MGJRouter registerURLPattern:@"mgj://category/家居" toHandler:^(NSDictionary *routerParameters) {
NSLog(@"routerParameters:%@", routerParameters);
}];
[MGJRouter openURL:@"mgj://category/家居"];
- Open 时,可以传一些 userinfo 过去
[MGJRouter registerURLPattern:@"mgj://category/travel" toHandler:^(NSDictionary *routerParameters) {
NSLog(@"routerParameters[MGJRouterParameterUserInfo]:%@", routerParameters[MGJRouterParameterUserInfo]);
// @{@"user_id": @1900}
}];
[MGJRouter openURL:@"mgj://category/travel" withUserInfo:@{@"user_id": @1900} completion:nil];
- 如果有可变参数(包括 URL Query Parameter)会被自动解析
[MGJRouter registerURLPattern:@"mgj://search/:query" toHandler:^(NSDictionary *routerParameters) {
NSLog(@"routerParameters[query]:%@", routerParameters[@"query"]); // bicycle
NSLog(@"routerParameters[color]:%@", routerParameters[@"color"]); // red
}];
[MGJRouter openURL:@"mgj://search/bicycle?color=red"];
- 定义一个全局的 URL Pattern 作为 Fallback
[MGJRouter registerURLPattern:@"mgj://" toHandler:^(NSDictionary *routerParameters) {
NSLog(@"没有人处理该 URL,就只能 fallback 到这里了");
}];
[MGJRouter openURL:@"mgj://search/travel/china?has_travelled=0"];
- 当 Open 结束时,执行 Completion Block
[MGJRouter registerURLPattern:@"mgj://detail" toHandler:^(NSDictionary *routerParameters) {
NSLog(@"匹配到了 url, 一会会执行 Completion Block");
// 模拟 push 一个 VC
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
void (^completion)() = routerParameters[MGJRouterParameterCompletion];
if (completion) {
completion();
}
});
}];
[MGJRouter openURL:@"mgj://detail" withUserInfo:nil completion:^{
[self appendLog:@"Open 结束,我是 Completion Block"];
}];
- 生成 URL
URL 的处理一不小心,就容易散落在项目的各个角落,不容易管理。比如注册时的 pattern 是 mgj://beauty/:id,然后 open 时就是 mgj://beauty/123,这样到时候 url 有改动,处理起来就会很麻烦,不好统一管理。
#define TEMPLATE_URL @"mgj://search/:keyword"
[MGJRouter registerURLPattern:TEMPLATE_URL toHandler:^(NSDictionary *routerParameters) {
NSLog(@"routerParameters[keyword]:%@", routerParameters[@"keyword"]); // Hangzhou
}];
[MGJRouter openURL:[MGJRouter generateURLWithPattern:TEMPLATE_URL parameters:@[@"Hangzhou"]]];
}
上面就是基本使用方法,github链接
组件化的拆分
实现组件化,最重要的是拆分,我们把整个项目拆分成各种的组件库,然后进行拼接使用。
- 基础组件库:
基础组件库放一些最基础的工具类,比如金额格式化、手机号/身份证/邮箱的有效校验,实质就是不会依赖业务,不会和业务牵扯的文件。 - 功能组件库:
分享的封装、图片的轮播、跑马灯功能、推送功能的二次封装,即开发一次,以后都能快速集成的功能。 - 业务组件库:
登录组件、实名组件、消息组件、借款组件、还款组件、各条产品线组件等。 - 中间件(组件通讯):
各个业务组件拆分出来后,组件之间的通讯、传参、回调就要考虑了,此时就需要一个组件通讯的工具类来处理。
CocoaPods远程私有库:
每个拆分出去的组件存在的形式都是以Pod的形式存在的,并能达到单独运行成功。 - 宿主工程:
宿主工程就是一个壳,在组件库中寻找这个工程所需要的组件,然后拿过来组装成一个App。
工程的搭建
iOS组件化的工程搭建都是基于cocoapods,下面的链接有详细的搭建流程:
组件化工程的搭建
dome链接moduleDome
以上就是组件化的基本研究,有什么问题请大家积极指出来,谢谢!