前言
虽然 iOS 组件化与路由的话题在业界谈了很久,但是貌似很多人都对其有所误解,甚至没搞明白“组件”、“模块”、“路由”、“解耦”的含义。
相关的博文也蛮多,其实除了那几个名家写的,具有参考价值的很少,况且名家的观点也并非都完全正确。架构往往需要权衡业务场景、学习成本、开发效率等,所以架构方案能客观解释却又带了些主观色彩,加上些个人特色的修饰就特别容易让人本末倒置。
所以要保持头脑清晰,以辩证的态度看待问题,以下是业界比较有参考价值的文章:
-
iOS应用架构谈 组件化方案[1]
-
蘑菇街 App 的组件化之路[2]
-
iOS 组件化 —— 路由设计思路分析[3]
-
Category 特性在 iOS 组件化中的应用与管控[4]
-
iOS 组件化方案探索[5]
本文主要是笔者对 iOS 组件化和路由的理解,力求以更客观与简洁的方式来解释各种方案的利弊,欢迎批评指正。
本文的 DEMO[6]
https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2FindulgeIn%2FYBRouterAndDecouplingDemo
一、组件与模块的区别
-
“组件”强调的是复用,它被各个模块或组件直接依赖,是基础设施,它一般不包含业务或者包含弱业务,属于纵向分层(比如网络请求组件、图片下载组件)。
-
“模块”强调的是封装,它更多的是指功能独立的业务模块,属于横向分层(比如购物车模块、个人中心模块)。
所以从大家实施“组件化”的目的来看,叫做“模块化”似乎更为合理。
但“组件”与“模块”都是前人定义的意义,“iOS 组件化”的概念也已经先入为主,所以只需要明白“iOS 组件化”更多的是做业务模块之间的解耦就行了。
二、路由的意义
首先要明确的是,路由并非只是指的界面跳转,还包括数据获取等几乎所有业务。
(一) 简单的路由
内部调用的方式
效仿 web 路由,最初的 iOS 原生路由看起来是这样的:
[Mediator gotoURI:@"protocol://detail?name=xx"];
缺点很明显:字符串 URI 并不能表征 iOS 系统原生类型,要阅读对应模块的使用文档,大量的硬编码。
代码实现大概就是:
+ (void)gotoURI:(NSString *)URI { // 解析 URI 得到目标和参数 NSString *aim = ...; NSDictionary *parmas = ...; if ([aim isEqualToString:@"Detail"]) { DetailController *vc = [DetailController new]; vc.name = parmas[@"name"]; [... pushViewController:vc animated:YES]; } else if ([aim isEqualToString:@"list"]) { ... } }
形象一点:
拿到 URI 过后,始终有转换为目标和参数 (aim/params
) 的逻辑,然后再真正的调用原生模块。显而易见,对于内部调用来说,解析 URI 这一步就是画蛇添足(casa 在博客中说过这个问题)。
路由方法简化如下:
+ (void)gotoDetailWithName:(NSString *)name { DetailController *vc = [DetailController new]; vc.name = name; [... pushViewController:vc animated:YES]; } </