一、概念
路由就是URL到函数的映射;对于客户端来说,就是把URL映射到响应的类或者controller。
如何能把URL映射到我们已经开发的类或者controller中呢,这就需要我们首先要为其添加一个配置表,该配置表定义了URL的规则和映射的目标。
二、路由能解决哪些问题
1、 降低App页面之间的耦合性。随着项目越来越负责,各个组件和页面之间的跳转也变得越来越复杂,他们之间的跳转逻辑也变的越来越紧密,这就造成了各个模块的相互依赖越来越紧密
2、 统一各个端(iOS、Android、Web)之间实现跳转页面的统一性
3、 可以通过动态下发配置表来配置App的跳转逻辑。这样iOS和Android两边只要共用一套配置文件,通过下发修改后配置表也能实现动态的改变映射目标。
三、资源的定义
在Android与iOS系统中,均支持URL Scheme,通过app内协定的Scheme,进行唤醒app和参数的传递。例如QQ(mqq://)、支付宝(alipay://)等。
四、实现的流程
1、路由的流程实现,如下图所示,我们确定路由需要做哪些事
1.1 A模块调用路由,为表达自己需要调用的是B模块,考虑到H5、推送以及其他App 的外部调用,可以使用URL这种方式来定义目标,也就是说用URL来表示目标B
1.2对一个URL的请求来说,路由需要有统一的回调处理,当然,如果不需要回调也是可以的,回调是需要目标去触发的
1.3路由要有处理URL的功能,并调用其他模块的能力
2、路由的逻辑结构
五、路由的使用
以下是我从Github上面找的一些路由方案,按照Star从高到低排列。依次来分析一下它们各自的设计思路。
1、WLRRoute
1.1类图结构
1.2 类说明:
(1)WLRRouteRequest,路由层的请求,无论是跨应用的外部调用还是内部调用,最后都形成一个路由请求,该请求包含了URL上的queryparameters和路径参数,还有内部调用时直接传入的原生参数,还有请求发起者对目标预留的回调block。
(2)WLRRouteHandler,路由层的handler处理,handler接收一个WLRRouteRequest对象,来完成是否是界面跳转,还是组件加载,还是内部逻辑。
(3)WLRRouter,路由核心对象,内部持有注册的Handler和匹配Matcher;负责界面跳转的Handler,负责组件加载的Handler,负责API的Handler;负责URL匹配的Matcher,则是则是处理请求函数和路径信息。路由的作用就是将外部调用传入的URL或者是内部调用传入的target,在内部匹配上对应的handler,然后调用生命周期方法,完成处理过程,当然,图中还有route的中间件,实际上是预留AOP的口子,方面后期扩展。
(4)WLRRouteMatcher,用以处理外部调用的URL是否能与预设的正则表达式匹配,每一个匹配规则对应一个WLRRouteMatcher, 在WLRRouter中,每一次注册一个规则都会生成一个WLRRouteHandler和WLRRouteMatcher与之对应。
(5)WLRRegularExpression,继承NSRegularExpression,用以匹配URL,WLRRouteMatcher内部有一个WLRRegularExpression对象,WLRRouteMatcher接受一个URL,会使用WLRRegularExpression生成一个WLRMatchResult对象,来确定是否匹配成功,如果匹配成果则将URL上的路径参数给取出来。
(6)WLRMatchResult,用以描述WLRRegularExpression的匹配结果,包含路径参数和请求参数。
1.3工作流程
(1)App启动实例化WLRRouter对象
(2)实例化WLRRouteHandler对象和匹配规则表达式
(3)WLRRouter对象挂载URL的表达式相对应WLRRouteHandler实例,同时根据匹配规则生成一个WLRRouteMatcher对象,用以处理符合此规则的URL。
(4)外部调用的URL和callback传入WLRRouter对象
(5)WLRRouter对象遍历内部持有的URL的匹配表达式,并找到每一个WLRRouteMatcher对象,将URL传入看是否能返回WLRRouteRequest对象
(6)将WLRRouteRequest对象传入对应的WLRRouteHandler对象。
(7)WLRRouteHandler对象根据WLRRouteRequest寻找到TargetViewController和SourceViewController,在生命周期函数里,完成参数传递与视图转场。
1.4调用实现
(1)实现WLRUserHandler继承WLRRouteHandler,用于实现目标对象处理跳转,主要对以下方法进行重写:
//handle处理一个请求
- (BOOL)shouldHandleWithRequest:(WLRRouteRequest *)request;
//根据request取出调用的目标视图控制器
-(UIViewController *)targetViewControllerWithRequest:(WLRRouteRequest *)request;
//根据request取出来源的视图控制器
-(UIViewController *)sourceViewControllerForTransitionWithRequest:(WLRRouteRequest *)request;
//开始进行转场
-(BOOL)transitionWithRequest:(WLRRouteRequest *)request error:(NSError *__autoreleasing *)error;
(2)注册路由规则和处理handle
self.router = [[WLRRouter alloc]init];
[self.router registerHandler:[[WLRUserHandler alloc]init] forRoute:@“/user/:phone([0-9]+)"];
也可以通过添加Block的为处理handle,如下
[self.router registerBlock:^WLRRouteRequest *(WLRRouteRequest *request) { return request } forRoute:@“/user/:phone([0-9]+)”];
说明:该request中含有URL匹配解析出的路由参数和请求参数,分别为routeParameters和queryParameters;另primitiveParams为原生调用时的传值
(3)当通过Scheme唤起app或者通过直接调用路由进行跳转时调用下面方法实现:
[self.router handleURL:[NSURL URLWithString:@“scheme://myapp/user/13500000000"] primitiveParameters:@{@"user":@"nihao"} targetCallBack:^(NSError *error, id responseObject) { NSLog(@"UserCallBack"); } withCompletionBlock:^(BOOL handled, NSError *error) { NSLog(@"UserHandleCompletion"); }];
其中primitiveParameters为app内调用跳转时指定数据,可为空;targetCallBack:为目标匹配的回调;withCompletionBlock:执行完跳转的回调。
2、JLRoutes
其本质可以理解为:保存一个全局的Map,key是url,value是对应存放block的数组,url和block都会常驻在内存中,当打开一个URL时,JLRoutes就可以遍历 , 这个全局的map,通过url来执行对应的block。
2.1 JLRoutes类说明
(1)routeControllersMap 是全局的单例可变字典。
(2)这个字典的 key 值对应一个标识,源码中称之为 scheme,为了不混淆,咱们就叫其为 JLRoutes 对象标识。这个标识对应的value 值为 routesController(JLRoutes类的对象:JLRoutes *routesController)。
(3)JLRoutes的对象(routesController)有很多属性,常用的有两个属性:NSString *scheme:也就是上面所说的 JLRoutes对象标识,也就是说,此 value 值记录了自己的 key 值。
(4)NSMutableArray *routes:此数组中存放了JLRRouteDefinition 对象。
(5)JLRRouteDefinition 对象为最终的具体模型,也就是说你注册的跳转逻辑的所有信息,都存在于这个模型中,包括要实施操作的handlerBlock(执行操作的block代码块)、scheme(JLRoutes对象标识)、pattern(模式)、priority(优先级)。
2.2工作流程
(1)App启动根据Scheme实例化JLRoutes对象和添加路由规则,一个规则对应一个JLRoutes对象,存储在全局routeControllersMap中。
(2)外部调用时,实例化JLRRouteRequest对象并解析出路径参数和请求参数存放在pathComponents和queryParams中。
(3)通过遍历routeControllersMap中的JLRoutes对象,找出能匹配URL规则的JLRoutes,同时返回匹配结果JLRRouteResponse。
(4)最后调用调用JLRoutes的处理结果的Block,由实现者实现目标的跳转。
2.3调用实现
(1)注册路由Scheme和添加路由规则
[[JLRoutes routesForScheme:@“RouteOne"]addRoute:@"/:phone" handler:^BOOL(NSDictionary * parameters) {
//处理匹配结果 进行跳转
return YES;
}];
(2)由JLRoutes调用routeURL传入URL,根据URL创建JLRRouteRequest并解析出路由路径和请求参数。
(3)匹配JLRoutes的匹配规则,取出路径上的有效参数
(4)调用跳转的处理Block,进行页面的目标跳转
3、WLRRoute 和 JLRoutes的差异化
3.1 JLRoutes
(1)缺乏规则匹配的能力,不支持正则表达式匹配。
(2)代码逻辑简单,容易上手,不需要重写处理类。
(3)路由规则跳转逻辑为Block实现。
(4)各个业务模块间的耦合性高。
(5)调用时需指定使用的Scheme,
3.2 WLRRoute
(1)具有强大的匹配能力,支持正则表达式的匹配。
(2)每个路由规则都对应一个Handle类,所以需重写其handle处理类,也可指定Block来定制跳转的目标页面。
(3)各个业务模块间的耦合性低
(4)调用时无需指定使用的Scheme,会自动匹配规则