一、项目背景
一套完备的路由方案是推进组件化进程前提,为了能够为所在的业务量身定做一套适合自己的路由方案,我做了一定的调研分析,我特意找了市场几款主流的路由方案进行简单的分析比对,希望可以集各家所长,它们有JLRouter、MGJRouter、CTMediator等,分析的比较浅陋,如有不准确的地方,可以及时留言指出;
二、JLRouter
JLRouter是一个纯粹的URL路由管理库,主要分为路由URL的注册、解析、匹配等功能,它以较少的代码处理了业务中最为复杂的URL规则。也就是说,JLRouter的URL规则非常灵活,可以应对最为复杂的业务场景,例如美团、阿里这种垂直业务繁多的App,每个业务线都可以用JLRouter灵活定制自己的URL规则。它具有如下特点:
1、URL解析规则遵循标准的NSURL格式
(备注:The URLs employed by the NSURL class are described in RFC 1808, RFC 1738, and RFC 2732.)
标准的NSURLComponent各个字段如下:
JLRouter会传入每个字符串,都会按照上面的样子进行切分处理,分别根据RFC的标准定义,取到各个NSURLComponent。
2、非常灵活的URL解析规则
(1)scheme名称可以有多个
iOS系统默认是支持URL Scheme的,就是我们可以根据tel://110这种格式像打开网页一样唤起并打开一个App,一款App的Scheme一般是唯一的。但是对于一些组件化程度非常高的场景,也许可以有多个Scheme的应用场景,例如,美团酒旅事业部,与美食事业部、结婚事业部、外卖事业部等垂直业务并行,在美团App里面有自己独立的业务入口,但是他们也有自己的独立App产品家族,此时酒旅事业部可以统一维护一个独立的路由scheme。
(2)ueser information + host + path区域字段可以灵活配置
接上,酒旅事业部下属也有酒店、景点、度假等子业务,子业务下面还有更细的子业务,这些子业务也可以维护自己的路由规则,所以他们可以在ueser information + host + path区域配置自己的路由规则;
3、JLRouter所有的跳转入口统一处理
JLRouter的跳转处理逻辑如下:
用openURL:方式打开url方式,可以将所有的入口集中在- (BOOL)application:openURL:options:options中处理,比较简单便捷;
4、灵活的优先级配置
每条路有都可以配置优先级,JLRouter查找时会优先匹配优先级高的路由;
5、JLRouter本身不提供跳转逻辑,具体的跳转逻辑需要业务方自己提供;
前面说过,JLRouter本身是一个单纯的URL路由管理功能,不提供viewController的push以及pop操作,这些代码需要App自己添加与处理;类似的如下:
6、JLRouter没有独立的正向传值和反向传值处理逻辑
如果A页面跳转到B页面,B需要A传递的值,这是需要业务自己写逻辑将A的值传递给B。如果A需要B的值回传,也需要业务自己写代码处理。业务自己添加代码处理,不同的开发人员可以灵活采取不同方式处理,会增加业务代码的复杂性。
7、JLRouter的注册逻辑比较灵活,业务可以在任意地方添加url注册代码
这就意味着,JLRouter注册逻辑在可以出现在业务代码的任意地方,不同的开发人员按照自己的需要和理解,添加自己的逻辑,这样对业务代码的侵入性更强,加大业务代码的复杂性。
8、JLRoutes路由的查找匹配逻辑效率不高
在JLRoutes类中,有以下代码:
for (JLRRouteDefinition *route in [self.mutableRoutes copy])
{
// check each route for a matching response
JLRRouteResponse *response = [route routeResponseForRequest:request decodePlusSymbols:shouldDecodePlusSymbols];
if (!response.isMatch) {
continue;
}
[self _verboseLog:@"Successfully matched %@", route];
if (!executeRouteBlock) {
// if we shouldn't execute but it was a match, we're done now
return YES;
}
// configure the final parameters
for循环遍历数组,时间复杂度O(n)。
9、参考资料
三、MGJRouter
MGJRouter是HHRouter的升级版,它本身也是一个纯粹的router,它的使用大致如下:
1、被跳转页面的注册:
2、入口处代码如下:
只要简单地完成以上两步,就可以页面的Router解析、数据传递、跳转;代码总体比较简单,url结构清晰。
与此同时,MGJRouter也支持url的注销操作,deregisterURLPattern:
3、MGJRouter的URL解析规则遵循标准的NSURL格式,这一点同JLRouter;
4、MGJRouter的支持参数的正向传递,block反向传递;
参数可以直接拼在url后面,也可以通过设置userInfo的方式。MGJRouter本身具有灵活的block传递功能;
5、MGJRouter的scheme格式也非常灵活,可以设置多个;
你可以注册mgj://的url或者mgj1://格式的url,均可跳转;另外,还支持中文的匹配(没啥意义);
6、MGJRouter的路由匹配是查找字典,时间复杂度:O(1),相比JLRouter较高;
7、MGJRouter的路由解析规则其实可以不仅限于viewcontroller,可以用于任意对象类的通讯;
8、MGJRouter页面路由的注册在业务页面中注册,意味着业务可以灵活配置自己的url规则,可以在+load或者其他较早的时机进行注册。
不利于url的统一管理,+load的无限制扩展,最终会给app启动带来负担;另外,由于业务的不断扩张,启动注册的页面越来愈多,注册表也会越来越大,这部分常驻的内存其实是一个不必要的开销;
参考文档:
四、CTMediator
1、CTMediator是一个基于Mediator模式和Target-Action设计模式,中间采取了runtime来完成调用而实现的组件库,这套组件化方案将远程调用和本地调用做了拆分,远程采取解析url的方式,本地调用采取Mediator方式进行调用,阿里的BeeHive也是参考了这类设计模式;
2、CTMediator无需启动注册,所以内存性耗比起MGJRouter要低,但是CTMediator采取了硬编码的方式给ViewController写一些“中介”代码,过程如下:
例如AViewController需要实现跳转解耦,首先需要在AViewController所在的项目中建立一个“接口”类:Target_A,这个类就是CTMediator查找AViewController的关键类:
- (UIViewController *)Action_viewController:(NSDictionary *)params
{
AViewController *viewController = [[AViewController alloc] init];
return viewController;
}
然后你还需要建立一个为AViewController所在项目服务的Pod:A_Category,针对AViewController的扩展代码:
- (UIViewController *)A_aViewControllerWithCallback:(void(^)(NSString *result))callback;
{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
params[@"callback"] = callback;
return [self performTarget:@"A" action:@"viewController" params:params shouldCacheTarget:NO];
}
当然以后AViewController中其他ViewController需要支持跳转,可以直接在Pod:A_Category中继续添加扩展方法。入口处跳转代码如下:
UIViewController *viewController = [[CTMediator sharedInstance] A_aViewControllerWithCallback:^(NSString *result) {
}];
[self.navigationController pushViewController:viewController animated:YES];
3、CTMediator在本地逻辑上加了url解析,即可实现远端调用。
4、CTMediator相比MGJRouter对业务代码的侵入性较小,但是由于需要额外硬编码一些“中介”代码,对App整体项目的侵入性更大(每个项目都要标配一个扩展)。
5、无需启动注册,利用runtime匹配目标viewcontroller,设计巧妙,内存查找性能更优,但是接入成本以及维护成本较urlmapping方式较大,后期的可扩展性也较差。
6、runtime找到目标viewcontroller,意味着业务viewcontroller方法的className命名必会受限,不够灵活。
7、远端调用和本地调用的区分,url统一注册的方式默认是没有作区分的,但是对于url调用来说,如果业务需要这其实就是一个参数的问题就能解决。
8、相比urlmapping注册的方式,启动的性耗,在我们工具类产品中,可以忽略不计,即使以后urlmapping文件膨胀了,我们也可以采取分级加载的方式来优化。
参考资料:https://casatwy.com/iOS-Modulization.html
五、MDPageMaster的使命
1、本身具有JLRouter和MGJRouter的基本的URL路由管理功能;由于小影家族的App和电商类App不同(拥有众多的垂直业务),工具类的页面VC不会有太多,Url不需要进行灵活的分级,要尽可能的简单易懂,数据格式可跨多平台,与android统一、H5统一;推荐格式如下:
mydemo://tools?id=123456&index=1
2、支持数据基本数据类型的参数的正向传递、反向传递(可引入ReactiveDataBoard);
3、支持app内部、外部跳转(是否需要区分内外部跳转),并且多种方式跳转的统一处理,可参考JLRouter;
4、统一的url、viewcontroller映射管理、归档功能,启动注册;
5、由于业务层navigationContorller的使用比较混乱(维护了多navigationContorller),MDPageMaster还要接管navigation功能,全局统一并且唯一的navigation,更好的navigation管理,可以支持常见的push、pop操作,以后可以支持CATransition等转场动画的定制;
6、即然具有了navigator的相关功能,也要对相同类页面的重复push堆栈进行管理,尤其针对首页需要有开关控制;
7、需要考虑到快速连续多次push页面、线程保护等问题;
8、向前兼容支持storyboard、xib等历史页面的跳转;
9、支持动态调度;
10、支持VC缓存等;
11、比较人性化的业务接入方式、比较灵活的业务可扩展性;