YZZRouter
为什么需要一个新路由器
JLRoutes
在 Github
中的 starred 数量最多。它支持一个 scheme 的多处理,但处理函数应该返回 Bool
来指示处理是否成功,所以在处理中不支持异步工作。并且 JLRoutes
最明显的缺陷是其匹配算法不够高效,它应该遍历在每次匹配时注册的所有路由。
其他 Routers
是为 VC 转换设计的,这在大多数情况下并不通用。
YZZRouter 的设计类似于 Web 侧的 Router。如果您曾经使用过 Express
或 Koa
等库,您会对 YZZRouter 感到熟悉。
YZZRouter 支持
- 高效(使用
trie
进行注册和匹配) - 异步工作
- 中间件
- 解析变量,处理通配符
如何安装
cocoapods
target 'MyApp' do
pod 'YZZRouter'
end
如何使用
注册
在注册之前,应该实例化RootRouter
类。由于最好通过不同的RootRouter
实例来隔离路由,因此YZZRouter不支持用于注册的类方法。例如,您有VC之间的转接路由,同时还有服务路由,应使用不同的实例而不仅仅是不同的方案。
RootRouter *route = [RootRouter router];
注册块处理器
[router add:@"aaht://example.com/newRouter" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
completion(RComplete);
}];
注册多个块处理器
RouterBlockHandle *handle1 = [[RouterBlockHandle alloc] initWithHandleBlock:^(RouterRequest *req, routerHandleCompletion completion) {
NSLog(@"hello world1");
completion(RNext);
}];
RouterBlockHandle *handle2 = [[RouterBlockHandle alloc] initWithHandleBlock:^(RouterRequest *req, routerHandleCompletion completion) {
NSLog(@"hello world2");
completion(RComplete);
}];
[router add:@"aaht://example.com/newRouter" handles:@[handle1, handle2]];
为多个URL注册处理器
[router add:@[@"aaht://test1", @"aaht://test2"] blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
completion(RComplete);
}];
按方案或公共前缀组织您的路由
//organize by scheme
Router *r = [router scheme:@"empty"];
[r add:@"/th/e/te/st/path" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
completion(RComplete);
}];
[r add:@"/th/e/te/:id" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
completion(RComplete);
}];
//organize by path
Router *pathR = [router path:@"empty://t/net"];
[pathR add:@"/f" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
completion(RComplete);
}];
[pathR add:@"/1/2/3" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
completion(RComplete);
}];
[router startUrl:@"empty://t/net/f" completion:nil];
支持变量
[router add:@"aaht://example.com/:id/:name/newRouter" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
[req.userInfo addEntriesFromDictionary:req.variables];
completion(RComplete);
}];
[router startUrl:@"aaht://example.com/123/hello/newRouter" autoRedirect:YES completion:^(RouterRequest *c) {
XCTAssertEqualObjects(c.variables, @{
@"id" : @"123",
@"name" : @"hello",
});
}];
**
使用三重通配符:**
作为多重通配符。多重通配符非常适用于具有相同模式的任务,如认证检查、日志、跟踪等。
例如:hhh://hel/**
,使用此URL注册的处理器将匹配任何以hhh://hel/
为前缀的URL。
一个非常通用的用途是在路由之前进行认证检查。可以使用路由器这样实现。
[router add:@"aaht:///**" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
if (!login) {
completion(RComplete);
} else {
completion(RNext);
}
}];
所有具有方案:"aaht"的URL都将检查是否已登录。如果没有登录,则不会执行左边的处理器。
///
表示此URL的主机为空。不要使用aaht://**
,因为在iOS标准库中解析它是非法的。
如果整个URL都是**
,则表示它是一个全局处理器,任何URL都将匹配,您可以使用这个来处理与每个路由相关的任务,如日志、跟踪。
将常用逻辑作为处理器提取
处理器在Web端和中间件有相同的概念。将您的常用逻辑提取为处理器是最佳实践,例如认证处理器、跟踪处理器、A/B测试处理器。
您可以使用YZZRouter提供的blockHandle或通过实现协议RouterHandleProtocol
来自定义您自己的处理器,该协议只有一个函数。
- (void)handleWithRequest:(RouterRequest *)req completion:(routerHandleCompletion)completion;
请求包含您在URL中需要的所有信息,如queryItems、变量和原始信息(如URL、方案、主机等)。此外,您还可以使用userInfo,这是一个可以存储您想要的任何内容的可变字典。
非常重要的一点是:记住在处理器中只调用一次completion()
。
completion
的参数是一个枚举。
typedef NS_ENUM(NSUInteger, RouterHandleResult) {
RNext,
RComplete,
RRedirect,
RFailed,
}
RNext
表示“下一个处理器将被执行”。
RComplete
表示终止处理器执行,剩下的处理器将不会执行。
RRedirect
如果您在处理器中更改了URL并希望开始一个新的匹配,则可以使用重定向。一个简单的示例,A/B测试处理器。
NSString *abtestUrl = @"abtest://nothing/hello";
NSString *helloabtestA = @"testScheme://nothing/hello/A";
NSString *helloabtestB = @"testScheme://nothing/hello/B";
NSString *abtestMock = helloabtestA;
[router add:abtestUrl blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
[req reuseWithUrl:abtestMock];
completion(RRedirect);
}];
[router add:helloabtestA blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
completion(RComplete);
}];
[router add:helloabtestB blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
completion(RComplete);
}];
// will match helloabtestA
[router startUrl:abtestUrl autoRedirect:YES completion:nil];
RFailed
将停止执行处理器,类似于RComplete
。
转移处理器
除了默认的blockHandle之外,YZZRouter还支持用于VC转换的转换处理器。您可以使用它如下
[router add:@"sf://A/main" handle:[[RouterTransferHandle alloc] initWithVCClass:MainViewController.class];
当您启动URL @"sf://A/main"
时,它将自动转换为MainViewController。将转换处理器作为URL注册中的最后一个处理器更好。
如果您想要更自定义的转换,只需遵守RouterHandleProtocol
并实现您自己的处理器。
高级用法
处理支持异步工作
handls support async work like network. Just Make sure completion called in proper time and must be called.
处理中的AOP
有一个简单的例子,每一项都会记录执行时长。
[router add:@"**" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
completion(RNext);
NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
NSLog(@"duration: %f", end - start);
}];
注意 completion(RNext)
,这个代码之前和之后的代码分别表示 在处理左边执行之前 和 在处理左边执行之后。这里的 completion()
并不代表返回,如果当前处理依赖于左边的执行结果,请将代码放在 completion(RNext) 之后。
白名单处理
在 RootRouter 中设置方案的白名单。
- (void)configSchemeWhiteList:(NSSet *)list;
或者使用 RouterWhiteListHandle
自定义白名单方式。
用全局 TrillingWildCard:
**
注册一个RouterWhiteListHandle
用于方案白名单。
调试
使用 Router 的功能。
- (NSDictionary *)debugRouterTrieTree;
以 Trietree
的形式查看所有注册信息。
使用 Router 的功能。
- (NSDictionary *)allRouteRegisterInfo;
您可以通过以下方式获取注册路径和处理程序:
"ROOT/**" = "RouterWhiteListHandle,RouterBlockHandle"; "ROOT/KDefaultSsheme!/t/net/1/2/3" = RouterBlockHandle;
RouterHandleProtocol
有一个可选的属性:name
。如果您实现这个属性,可以像之前一样获取这些信息,否则它会返回处理程序类的字符串。
其它内容
问题和MR都欢迎!