组件化

# 为什么要组件化

1.实现之间解耦、减少项目的编译时间,提升业务开发效率

2.模块拆分成组件,方便对外付能

## 组件化方案

1.代码解耦

1)组件原则

组件通常分为两种类型的组件:基础组件,业务组件。

*业务组件依赖基础组件

*基础组件不可依赖业务组件。

*业务组件间不可相互依赖。

2)组件间的通信方式:

组件间通信方式,业内主要有两种实现方式:

1.协议式框架,比如蘑菇街的这种方案。蘑菇街 App 的组件化之路。不建议

2. 中间者架构,比如casatwy的方案

casatwy的方案-基于Category和Target-Action

主要是基于Mediator模式和Target-Action模式,中间采用了runtime来完成调用。通过mediator的Category来输出组件的对外调用方法。使用同一种方案,实现组件间调用。

优点:

1)不需要注册URL

2)使用同一种方案,基于Target-Action模式实现组件间的调用。

3)区分app内部调用和外部调用。

4)同一种方法可以实现简单参数和复杂参数的调用。

2.版本管理

版本管理使用 cocoapods,每个组件都拆成独立的pod库,并生成一个配置表,来进行组件间的依赖组件的版本管理。

1.通过 Cocoapods 搭建私有库,创建相应的模版。

2.不推荐 Cocoapods 编译二进制文件,自己写脚本编起来更灵活。

3.自定义 gem,完成 podspec 源码二进制切换。

4.二进制文件(组件编译为静态包)存储到内部云。

5.使用工具/脚本管理 podfile。

6.系统管理 pod 库。

工程结构

通过 Cocoapods 搭的自定义库,自定义模版。

每个同学拿到的组件都是一个相同结构的工程,所需要做的工作就是在相应的 Pods/Developemnt Pods/ 组件 /Classes 下编码,组件输出类通过模版创建,可在相应的类里使用 JDROUTER_EXTERN_METHOD 提供接口。

## 组件化通信

1、URL-Block的方式 蘑菇街为代表

这种方式的核心是预先将url对应的方法先注册给中间人,然后调用方调用的时候,中间人就会去哈希表或者数组里找到对应的方法进行调用。

总结:

1、被调用方需要提前register相应的block才能调用,而且被注册的block似乎并没有很好的释放时机?内存方面是否存在问题。

2、调用方和被调用方都需要依赖中间件。

3、单纯的URL拼接的方式并没有办法满足复杂的参数传递,所以才会衍生多种调用的方法。

2、Protocol-Class的方式

这种方案类似于第一种方案,它是保存和Protocol和Class的映射关系,每次调用方通过中间人拿到注册时候的Class,直接通过对象进行方法调用;

[Router registerClass:[SomeClass Class] forProtocol:aProtocol];

 id someOne  =[Router classForProtocol:aProtocol];

 [someOne call];


 总结:

 1、类似于block的注册方式,也需要提前register对象。

 2、调用双方都需要依赖中间管理类。

 3、调用方式是直接调用,比较符合常用习惯。


 3、Target-Selector的方式

 这个方案是由CTMediator的作者提出的,他指出了一些URL-Block方式的缺点,然后提出了这种方案。

 这个方案的核心就是我们平常使用的performSelector的方式。


 // 远程App调用入口

 -(id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;

 // 本地组件调用入口

 -(id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;

 -(void)releaseCachedTargetWithTargetName:(NSString *)targetName;


 中间人负责找到具体的target,然后实现方法转发。这个方式还强调了要区分本地调用和远程调用的方式,以防止黑客直接通过浏览器或OpenUrl的方式直接访问到APP内部模块。

 核心实现主要是:

 -(id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget

 {

     NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];

     "把对象名转换为相应的类"

     // generate target

     NSString *targetClassString = nil;

     if (swiftModuleName.length > 0) {

         targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];

     } else {

         targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];

     }

     NSObject *target = self.cachedTarget[targetClassString];

     if (target == nil) {

         Class targetClass = NSClassFromString(targetClassString);

         target = [[targetClass alloc] init];

     }

     // generate action

     NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];

     SEL action = NSSelectorFromString(actionString);


     if (target == nil) {

         // 这里是处理无响应请求的地方之一,这个demo做得比较简单,如果没有可以响应的target,就直接return了。实际开发过程中是可以事先给一个固定的target专门用于在这个时候顶上,然后处理这种请求的

         [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];

         return nil;

     }


     if (shouldCacheTarget) {

         self.cachedTarget[targetClassString] = target;

     }

     if ([target respondsToSelector:action]) {

         "消息转发,safePerformAction使用NSInvocation进行转发"

         return [self safePerformAction:action target:target params:params];

     } else {

         // 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理

         SEL action = NSSelectorFromString(@"notFound:");

         if ([target respondsToSelector:action]) {

             return [self safePerformAction:action target:target params:params];

         } else {

             // 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。

             [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];

             [self.cachedTarget removeObjectForKey:targetClassString];

             return nil;

         }

     }

 }

 总结:

 1、需要额外维护中间类拓展,只有调用方需要引用中间类。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容