今天來聊聊組件化保屯,之前一直聽說大廠在搞,什么淘寶架構(gòu),什么蘑菇街配椭,既然談到了架構(gòu)的問題虫溜,那必屬重中之重。接下來分析一下蘑菇街開源的代碼股缸,自己做個總結(jié)衡楞。
引入
類書本的文章個人感覺還是寫不來的,再搬到自己寫的東西這來也不合適敦姻,所以直接上一鏈接瘾境,通過鏈接文章大致可了解下它的前身后世,產(chǎn)生原因镰惦,以及整體宏觀架構(gòu)設(shè)計迷守,而我接下來要做的是細(xì)化,以及轉(zhuǎn)化旺入,便于自己吸收 ---------> 組件化架構(gòu)漫談
1. 話不多說兑凿,先看入口:

image_1c25qj3m5ejq111g1v5lvkgjek9.png-7.7kB
@interface MGJRouter ()
/**
* 保存了所有已注冊的 URL
* 結(jié)構(gòu)類似 @{@"beauty": @{@":id": {@"_", [block copy]}}}
*/
@property (nonatomic) NSMutableDictionary *routes;
@end
+ (instancetype)sharedInstance
{
static MGJRouter *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
很明顯,蘑菇街架構(gòu)(以下簡稱MGJ)通過該單例作管理茵瘾,統(tǒng)一進(jìn)行調(diào)配礼华,而該單例僅有一個變量,就是routes拗秘,實際上它僅僅是管理了一個字典的結(jié)構(gòu)圣絮,具體字典內(nèi)有哪些內(nèi)容,我們慢慢看雕旨;
2. 回調(diào)Block的定義
/**
* routerParameters 里內(nèi)置的幾個參數(shù)會用到上面定義的 string
*/
typedef void (^MGJRouterHandler)(NSDictionary *routerParameters);
/**
* 需要返回一個 object扮匠,配合 objectForURL: 使用
*/
typedef id (^MGJRouterObjectHandler)(NSDictionary *routerParameters);
上面這兩個block定義是MGJ注冊URL的回調(diào),一個帶返回值凡涩,另一個不帶棒搜,在這里我們說一下帶返回值的block用法;如下舉例 ------>

WX20180107-134538@2x.png-37.5kB
//聲明
typedef UIViewController *(^ViewControllerHandler)();
//作參數(shù)
@interface DemoListViewController : UIViewController
+ (void)registerWithTitle:(NSString *)title handler:(ViewControllerHandler)handler;
@end
//定義
@implementation DemoListViewController
+ (void)registerWithTitle:(NSString *)title handler:(ViewControllerHandler)handler
{
UIViewController* vc = handler()
}
@end
//在別處調(diào)用
@implementation DemoDetailViewController
[DemoListViewController registerWithTitle:@"基本使用" handler:^UIViewController *{
return DemoDetailViewController();
}];
@end
如上活箕,我們把ViewControllerHandler
的運(yùn)行延遲到了實際調(diào)用的時刻帮非,并且我們可以在這個handler的實現(xiàn)中帶入很多信息;
3. MGJ數(shù)據(jù)結(jié)構(gòu)管理
extern NSString *const MGJRouterParameterURL;
extern NSString *const MGJRouterParameterCompletion;
extern NSString *const MGJRouterParameterUserInfo;
//*************************************************
static NSString * const MGJ_ROUTER_WILDCARD_CHARACTER = @"~"; //這是一個占位符
static NSString *specialCharacters = @"/?&.";
NSString *const MGJRouterParameterURL = @"MGJRouterParameterURL";
NSString *const MGJRouterParameterCompletion = @"MGJRouterParameterCompletion";
NSString *const MGJRouterParameterUserInfo = @"MGJRouterParameterUserInfo";
從這里我們可以看出讹蘑,MGJ的路由管理,實際上是一個解析url以及對應(yīng)的管理筑舅,我們舉幾個URL來看一下:
@"mgj://"
@"mgj://foo/bar/none/exists"
@"mgj://foo/bar"
@"mgj://category/家居"
@"mgj://category/travel"
@"mgj://search/:query"
@"mgj://detail"
@"mgj://search/:keyword"
@"mgj://search_top_bar"
通過上面的URL我們可以看出座慰,路由的管理實際上就是url的解析過程,下面我們來具體看一下解析過程翠拣;
4. URL解析
- route url

WX20180107-150437@2x.png-59.1kB
- (NSArray*)pathComponentsFromURL:(NSString*)URL
{
NSMutableArray *pathComponents = [NSMutableArray array];
if ([URL rangeOfString:@"://"].location != NSNotFound) {
NSArray *pathSegments = [URL componentsSeparatedByString:@"://"];
// 如果 URL 包含協(xié)議版仔,那么把協(xié)議作為第一個元素放進(jìn)去
[pathComponents addObject:pathSegments[0]];
// 如果只有協(xié)議,那么放一個占位符
URL = pathSegments.lastObject;
if (!URL.length) {
[pathComponents addObject:MGJ_ROUTER_WILDCARD_CHARACTER];
}
}
for (NSString *pathComponent in [[NSURL URLWithString:URL] pathComponents]) {
if ([pathComponent isEqualToString:@"/"]) continue;
if ([[pathComponent substringToIndex:1] isEqualToString:@"?"]) break;
[pathComponents addObject:pathComponent];
}
return [pathComponents copy];
}
-
key-value
WX20180107-151956@2x.png-111.8kB
- (NSMutableDictionary *)addURLPattern:(NSString *)URLPattern
{
NSArray *pathComponents = [self pathComponentsFromURL:URLPattern];
NSMutableDictionary* subRoutes = self.routes;
for (NSString* pathComponent in pathComponents) {
if (![subRoutes objectForKey:pathComponent]) {
subRoutes[pathComponent] = [[NSMutableDictionary alloc] init];
}
subRoutes = subRoutes[pathComponent];
}
return subRoutes;
}
- 核心url解析

WX20180107-160319@2x.png-147.8kB
- (NSMutableDictionary *)extractParametersFromURL:(NSString *)url
{
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
parameters[MGJRouterParameterURL] = url;
NSMutableDictionary* subRoutes = self.routes;
NSArray* pathComponents = [self pathComponentsFromURL:url];
BOOL found = NO;
// borrowed from HHRouter(https://github.com/Huohua/HHRouter)
for (NSString* pathComponent in pathComponents) {
// 對 key 進(jìn)行排序,這樣可以把 ~ 放到最后
NSArray *subRoutesKeys =[subRoutes.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
return [obj1 compare:obj2];
}];
for (NSString* key in subRoutesKeys) {
if ([key isEqualToString:pathComponent] || [key isEqualToString:MGJ_ROUTER_WILDCARD_CHARACTER]) {
found = YES;
subRoutes = subRoutes[key];
break;
} else if ([key hasPrefix:@":"]) {
found = YES;
subRoutes = subRoutes[key];
NSString *newKey = [key substringFromIndex:1];
NSString *newPathComponent = pathComponent;
// 再做一下特殊處理蛮粮,比如 :id.html -> :id
if ([self.class checkIfContainsSpecialCharacter:key]) {
NSCharacterSet *specialCharacterSet = [NSCharacterSet characterSetWithCharactersInString:specialCharacters];
NSRange range = [key rangeOfCharacterFromSet:specialCharacterSet];
if (range.location != NSNotFound) {
// 把 pathComponent 后面的部分也去掉
newKey = [newKey substringToIndex:range.location - 1];
NSString *suffixToStrip = [key substringFromIndex:range.location];
newPathComponent = [newPathComponent stringByReplacingOccurrencesOfString:suffixToStrip withString:@""];
}
}
parameters[newKey] = newPathComponent;
break;
}
}
// 如果沒有找到該 pathComponent 對應(yīng)的 handler益缎,則以上一層的 handler 作為 fallback
if (!found && !subRoutes[@"_"]) {
return nil;
}
}
// Extract Params From Query.
NSArray<NSURLQueryItem *> *queryItems = [[NSURLComponents alloc] initWithURL:[[NSURL alloc] initWithString:url] resolvingAgainstBaseURL:false].queryItems;
for (NSURLQueryItem *item in queryItems) {
parameters[item.name] = item.value;
}
if (subRoutes[@"_"]) {
parameters[@"block"] = [subRoutes[@"_"] copy];
}
return parameters;
}
+ (void)openURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo completion:(void (^)(id result))completion
{
URL = [URL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *parameters = [[self sharedInstance] extractParametersFromURL:URL];
[parameters enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) {
if ([obj isKindOfClass:[NSString class]]) {
parameters[key] = [obj stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
}];
if (parameters) {
MGJRouterHandler handler = parameters[@"block"];
if (completion) {
parameters[MGJRouterParameterCompletion] = completion;
}
if (userInfo) {
parameters[MGJRouterParameterUserInfo] = userInfo;
}
//所以注冊路由時的回調(diào)是在這里才調(diào)用到的
//也就是openURL響應(yīng)了register的回調(diào)
if (handler) {
[parameters removeObjectForKey:@"block"];
handler(parameters);
}
}
}
5. 后續(xù)?