前言
有一段时间没有更新文章了,之前心里默默给自己定下一个小目标,每周更新一片文章。一方面激励自己不要懈怠,另一方面也记录平时开发中的点滴。后面项目一忙,加班了一段时间也就没有时间更新了(还是对自己要求不够高啊), 业余的时间都花在了王者农药上,(再次谴责下自己)
最近新的项目中使用了混合开发,一直在学习react-Native
,但是关于组件化,我也实践了一段时间,这里说下我是怎么处理组件间交互的。话不多说,直接开干。
组件间交互的几种方法。
- urlRoute 通过注册url协议确定跳转路由的方式,(貌似蘑菇街就是使用的这种方式,)
- action-Target 就是通过RunTime机制,动态调用方法,
一般iOS实现组件化,都是使用这两种方式的其中一种,至于哪一种方式更好一点,我个人觉得action-Target
这种方式更简洁明了一点,不像urlRoute
需要配置复杂的协议以及路由表。我们项目也是以action-Target
为基础探索组件化的。。
组件间的划分。
我们是以业务来划分组件的,将单独的某一个业务拆分成一个组件,其中有一个主组件来负责,调度各个组件的数据分发,以及处理一些公共的事件。(比如错误处理,网络监听,登录状态等),整个架构如下
- 其中
Base
里面有一些基类,BaseController
,BaseViewModel
等基本的公共操作
@class QFBBaseViewModel;
@interface QFBBaseViewController : UIViewController
/**
* viewModel
*/
@property (strong, nonatomic, readonly) QFBBaseViewModel *viewModel;
- (instancetype)initWithViewModel:(QFBBaseViewModel *)viewModel;
- (void)bindViewModel;
- (void)popCallBack:(NSDictionary *)infoDic;
@end
在初始化每一个Controller时需要绑定ViewModel
@interface QFBBaseViewModel : NSObject <GlobalStyleProtocol>
/** 用户当前状态 */
@property (nonatomic, strong, readonly) RACCommand *userStatusCommad;
/** 网络状态 */
@property (nonatomic, assign) QFNetworkStatus netWorkStatus;
// 接口错误信息 在子类设置某个接口错误时显示的错误信息
@property (nonatomic, copy) NSString *errorMessage;
/** 数据请求 */
@property (strong, nonatomic, readonly) RACCommand *requestDataCommand;
/** DP层的服务对象 */
@property (nonatomic, strong, readonly) id<DataDisposeServiceProtocol> services;
- (instancetype)initWithService:(id<DataDisposeServiceProtocol>)service;
- (instancetype)initWithDefaultService;
- (void)initialize;
- (RACSignal *)executeRequestDataSignal:(id)input;
@end
这个ViewModel
一个最重要的作用就是来处理统一的网络状态更新时,处理每一个页面的数据刷新,以及导航栏状态的更新。
- 在
Base
组件中有一个最重要的路由表,组件间的通信也就是根据这个路由表来达到的。
我的架构思路是:路由表是一个Protocol
在这个协议中配置好跨模块之间的通信信息传递。然后通过action-Target
路由器去实现这个协议,那么在子组件内去实现这个协议对应的方法。就达到了跨模块之间的数据传递。这样组件间的依赖关系就变为单项依赖,也就是所有的子组件只依赖于主组件。
如: 在Base
模块的协议表中定义
@protocol beyondActionProtocol <NSObject>
@optional
/* 跳转报备模块,新增报备 <ReportClientController.h> */
- (void) pushToAddNewReportControllerWithData:(id)data;
- (void) popAddNewReportController:(id)data;
@end
那么在房源模块只需要调用。
[[MediatorAction sharedInstance] pushToAddNewReportControllerWithData:dic];
而在目标模块 也就是报备模块只需要遵守协议并实现相应方法。
//************************ 跨模块跳转 ************************/
- (void)pushToAddNewReportControllerWithData:(id)data {
NSDictionary *dict = data;
ReportClientViewModel *viewModel = [[ReportClientViewModel alloc]initWithDefaultService];
viewModel.reporterData = dict;
id vc = [@"ReportClientController" VKCallClassAllocInitSelectorName:@"initWithViewModel:" error:nil, viewModel];
UIViewController *currentVC = [self performTarget:nil action:nil];
if ([vc isKindOfClass:[UIViewController class]]) {
// UIViewController *vccontoleer = vc;
QFBaseNavigationController *nav = [[QFBaseNavigationController alloc]initWithRootViewController:vc];
// vccontoleer.modalPresentationStyle = UIModalPresentationFullScreen;
// vccontoleer.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[currentVC presentViewController:nav animated:YES completion:nil];
// [currentVC.navigationController pushViewController:vc animated:YES];
}
}
- (void)popAddNewReportController:(id)data {
UIViewController *currentVC = [self performTarget:nil action:nil];
[currentVC.navigationController popViewControllerAnimated:YES];
// 返回时调用哪个页面的回调方法
// [@"MyProfileViewController" VKCallClassAllocInitSelectorName:@"popCallBack:" error:nil,data];
}
后记
在实际开发中,模块之间的调用并没有这么简单,还需要处理很多容错机制,以及Base
组件的瘦身等。我这里只是举一个简单的例子来说明我们项目中我对组件间访问的方法。也希望能帮到需要的人,如果你有更好的方式,也希望(恳请)你能分享你的思路。
关于组件化,近期可能就不研究了。后期会慢慢更新一些在学习React-Navite
的一些误区和基本知识。