一、为什么需要路由
首先我们应该清楚为什么要组件化开发,它将
复杂项目拆分出各个模块
,有助于多人大型团队的协同开发。在这里
各个模块可以看作是一个独立的app
,但随着业务的发展,系统变得越来越复杂,项目内各模块之间耦合严重, 这对代码质量,以及开发效率都会造成很大的影响。
为了解决这个问题,路由这个东西就应运而生了;它主要是做啥呢,其实就相当于
各个模块之间的中转处理
,不用各个模块之间直接调用了。这样才是真正意义上的组件化。
二、路由设计
先上路由设计图
1、路由大量应用于前端页面,通过把一个 URL 与一个页面绑定,需要时通过 URL可以方便的打开相应页面。
// 通过路由URL跳转到AViewController
AViewController *a = routerFromUrl(@"https://h5.m.cn/AViewController");
[self.navigationController pushViewController:a animated:true];
有些页面需要传递参数,URL 协议支持基本类型参数
BViewController *b = routerFromUrl(@"https://h5.m.cn/BViewController?name=123");
[self.navigationController pushViewController:b animated:true];
复杂类型的参数,可以提供一个额外的字典参数 nativeParams, 将复杂参数放到字典中即可:
2、内部实现:路由文件结构
PQRouter:入口文件,集中处理逻辑
.h
// 简单调用
extern id routerFromUrl(NSString *url) ;
// 带有复杂参数
extern id routerFromUrlWithNativeParams(NSString *url, NSDictionary *nativeParams) ;
@interface PQRouter : NSObject
@end
@interface NSObject(PQRouter) <PQRouterProtocol>
+ (PQRouterResponse *) handleUrlWithRequest:(PQRouterRequest *) request ;
@end
@interface UIViewController(PQRouter) <PQRouterProtocol>
- (instancetype) initWithRequest:(PQRouterRequest *) request ;
@end
.m
#import "PQRouter.h"
#import <objc/runtime.h>
#import "PQRouterRequest.h"
@implementation PQRouter
// 解析url
NSDictionary *parseUrl(NSString *url) {
if (!url || url.length <= 0) {
return nil ;
}
NSArray *urls = [url componentsSeparatedByString:@"?"] ;
NSString *host = [urls firstObject] ;
NSString *handle = [[host componentsSeparatedByString:@"/"] lastObject] ;
NSMutableDictionary *params = [NSMutableDictionary dictionary] ;
if (urls.count > 1) {
NSString *urlParamString = urls.lastObject ;
NSArray *keyValueArray = [urlParamString componentsSeparatedByString:@"&"] ;
for (NSString *kvString in keyValueArray) {
NSArray *kvs = [kvString componentsSeparatedByString:@"="] ;
if (kvs.count == 2) {
params[kvs[0]] = [kvs[1] stringByRemovingPercentEncoding] ;
}
}
}
return @{@"handle": handle,@"host": host, @"params": params} ;
}
// 生成PQRouterRequest请求对象
PQRouterRequest *requestWithUrl(NSString *url, NSDictionary *nativeParams) {
NSDictionary *hostParamsMapping = parseUrl(url) ;
NSString *handle = hostParamsMapping[@"handle"] ;
NSString *host = hostParamsMapping[@"host"] ;
NSMutableDictionary *params = hostParamsMapping[@"params"] ;
PQRouterRequest *request = [[PQRouterRequest alloc] init] ;
request.host = host ;
request.params = params ;
request.nativeParams = nativeParams ;
request.url = url ;
request.handler = handle;
return request ;
}
id routerFromUrl(NSString *url) {
return routerFromUrlWithNativeParams(url,nil);
}
id routerFromUrlWithNativeParams(NSString *url, NSDictionary *nativeParams) {
PQRouterRequest *request = requestWithUrl(url,nativeParams) ;
Class c = NSClassFromString(request.handler) ;
if (c && [c respondsToSelector:NSSelectorFromString(@"handleUrlWithRequest:")]) {
PQRouterResponse *response = [c handleUrlWithRequest:request] ;
if (response.toHandler) {
return response.toHandler;
}
}
return [[c alloc] init] ;
}
@end
@implementation NSObject(PQRouter)
+ (PQRouterResponse *) handleUrlWithRequest:(PQRouterRequest *) request {
PQRouterResponse *response = [[PQRouterResponse alloc] init] ;
if ([self isKindOfClass:object_getClass(UIViewController.class)]) {
response.toHandler = [[self alloc] initWithRequest:request] ;
} else {
response.toHandler = [[self alloc] init] ;
}
return response ;
}
@end
static char kRouterRequestKey = '\0' ;
@implementation UIViewController(PQRouter)
- (instancetype) initWithRequest:(PQRouterRequest *) request {
self = [self init];
if (self) {
objc_setAssociatedObject(self, &kRouterRequestKey, request, OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
}
return self ;
}
@end
PQRouterRequest:路由统一请求对象
@interface PQRouterRequest : NSObject
@property (nonatomic, copy) NSString *host ;
@property (nonatomic, copy) NSString *url ;
@property (nonatomic, copy) NSString *handler ;
@property (nonatomic, strong) NSDictionary *params ;
@property (nonatomic, strong) NSDictionary *nativeParams ;
@end
PQRouterResponse:路由统一返回对象
@interface PQRouterResponse : NSObject
@property (nonatomic, strong) id toHandler ;
@end
PQRouterProtocol:模块需要遵循的协议
@protocol PQRouterProtocol <NSObject>
@optional
+ (id) handleUrlWithRequest:(PQRouterRequest *) request ;
@end
3、案例
+ (id)handleUrlWithRequest:(PQRouterRequest *)request {
NSLog(@"%@",request.params);
BViewController* controller = [[BViewController alloc] init];
PQRouterResponse *response = [[PQRouterResponse alloc] init] ;
response.toHandler = controller;
return response;
}
/**
* 跳转B
*/
- (IBAction)pushB:(id)sender {
// 通过路由URL跳转到BViewController并带有name参数
BViewController *b = routerFromUrl(@"https://h5.m.cn/BViewController?name=123");
[self.navigationController pushViewController:b animated:true];
}
// BViewController打印结果,可见参数带过来了
2020-06-07 17:29:42.487097+0800 ModuleProject[4530:175751] {
name = 123;
}
才疏学浅,如有不正确的地方请多多指教。推荐几个比较好的博客供大家学习
iOS应用架构谈 组件化方案
滴滴的组件化实践与优化
京东 iOS 客户端组件管理实践