• Stars
    star
    669
  • Rank 67,189 (Top 2 %)
  • Language
    Objective-C
  • License
    MIT License
  • Created about 6 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

iOS 组件化开发项目架构设计,结合 MVVM 设计模式 + RAC 数据绑定 + Pod 组件管理, 实现一套实战性的iOS组件化架构

iOS从零到一搭建组件化项目架构

随着公司业务需求的不断迭代发展,工程的代码量和业务逻辑也越来越多,原始的开发模式和架构已经无法满足我们的业务发展速度了,这时我们就需要将原始项目进行一次重构大手术了。这时我们应该很清晰这次手术的动刀口在哪,就是之前的高度耦合的业务组件和功能组件,手术的目的就是将这些耦合拆分成互相独立的各个组件。

工程效果预览

image

掘金专栏文章详解

下面我们围绕这几个问题来展开讲解

  • 为什么要用组件化,它给我们带来哪些优势
  • 各个组件该如何进行拆分,拆分的颗粒度该如何控制
  • 如何从零到一搭建组件化架构项目

为什么要用组件化

我们先来张图看看在没有使用组件化前,我们各个模块间的依赖关系

image

从上面这种各个业务组件的依赖关系来看,他们是互相依赖的,业务组件和业务组件间产生了严重的耦合关系,这样一来对我们工程的扩展性就会大大的降低,维护成本就会变高。

举个例子:假设某天产品经理说,咱们公司的业务发展的太好了,咱们的营销模块需要独立出来成一个单独的应用,以便于咱们可以添加更多高效的营销手段。这时我们就傻眼了,需要独立出一个app出来,这可怎么搞啊,营销模块的代码和其他的很多业务代码耦合在一起了,现在要独立出来,那就只能重新写一个营销应用了,之前的代码剥离不干净了。

从上面我们列举的一个简单的例子可以体会到:在项目没有做到真真意义上的组件化之前,各个业务模块和业务模块间的高度耦合,功能组件和功能组件间的高度耦合对未来公司的业务扩展来说,成本很高,不能做到同样业务逻辑的代码的高度复用,这样对我们开发来说也是效率的降低。

好了,有的同学可能会说,既然上面各个模块间耦合这么高,那我就来将这些耦合解耦,于是,可能会出现下面这张图的模块间的关系。

image

从下面这张图来看,我们发现,现在确实能做到各个业务模块间完全的解耦了,他们不再互相依赖了,同时我们引入了一个中间调度者的一个角色,现在是各个业务模块和这个中间调度者角色产出了严重的依赖。我们思考下发现,我们的各个业务模块依赖这个中间调度者,这个是完全正常的,因为他们需要这个调度者来做统一的事件分发工作,但是这个调度者却又依赖了每个业务模块,这层依赖是有必要的吗?我们回头想想真正的组件化开发是完全的去依赖化,这个依赖是完全没有必要的。例如:假设我们现在有一个新的B APP需要开发,这时我们也需要用到这个中间调度者组件,但是我们不能直接拿过来用,因为它又依赖了很多A App的业务组件。因此,我们的组件化架构设计又需要一次升级变更了,升级成如下图所示的模型。

image

从上面的这张图,我们可以看出,各个业务模块间只会依赖中间调度者,并且中间调度者不对各个模块产生任何的依赖。

好了,从上面的三张图之间的对比,我们就可以很好的理解为什么我们的工程急需要实现组件化架构开发了,以及各自的优劣势。

各个组件该如何进行拆分

关于组件该如何拆分,这个没有一个完整的标准,因为每个公司的业务场景不一样,对应衍生出来的各个业务模块也就不一样,所以业务组件间的拆分,这个根据自己公司的业务模块来进行合理的划分即可。这里我们来说下整个工程的组件大致的划分方向

  1. 项目主工程:当我们工程完全使用组件化架构进行开发后,我们会惊奇的发现我们的主工程就成了一个空壳子工程。因为所有的主工程呈现出来的内容都被拆分成了各个独立的业务组件了,包括各个工具组件也是各自互相独立的。这样我们发现开发一个完整的APP就像是搭建乐高积木一样,各个部件都有,任我们随意的组合搭建,这样是不是感觉很爽。
  2. 业务组件:业务组件就是我们上面示例图所示的各个独立的产品业务功能模块,我们将其封装成独立的组件。例如示例Demo中的电子发票业务组件,业务组件A,业务组件B。我们通过组装各个独立的业务组件来搭建一个完整的APP项目。
  3. 基础工具类组件:基础工具类是各个互相独立,没有任何依赖的工具组件。它们和其它的工具组件、业务组件等没有任何依赖关系。这类组件例如有:对数组,字典进行异常保护的Safe组件,对数组功能进行扩展Array组件,对字符串进行加密处理的加密组件等等。
  4. 中间件组件:这个组件比较特殊,这个是我们为了实现组件化开发而衍生出来的一个组件,上面示例图中的中间调度者就是一个功能独立的中间件组件。
  5. 基础UI组件:视图组件就比较常见了,例如我们封装的导航栏组件,Modal弹框组件,PickerView组件等。
  6. 业务工具组件:这类组件是为各个业务组件提供基础功能的组件。这类组件可能会依赖到其他的组件。例如:网络请求组件,图片缓存组件,jspatch组件等等

至于组件的拆分颗粒度,这个着实不好去断定,因人而异,不同的需求功能复杂度拆分出来的组件大小也不尽相同

如何从零到一搭建组件化架构

在讲如何从零到一来实现一个组件化架构项目前,我们需要熟练掌握使用pod来制作组件库。下面我们就围绕提供的组件化示例项目来展开讲解。

首先,我们来看示例Demo中包含哪些业务组件(如下图所示:):

image

示例Demo中,我提供了三个业务组件来作为演示效果,其中业务模块A和业务模块B是临时业务模块组件,电子发票业务组件时真实的企业需求功能组件。

我们再来看下示例Demo中都提供了哪些工具组件(如下图所示)

注意了:这里提供的6个工具组件也都是作者已经封装好的功能组件,大家也可以直接 install 安装使用的哦。

image

详细操作步骤

第一步:

我们先创建一个空的iOS工程项目:MainProject,这个空项目作为我们的主工程项目,就是上面所说的壳子工程项目,然后初始化pod,这里不清楚pod的使用的小伙伴们请自行查阅资料。

第二步:

我们创建一个空工程项目:ModuleA,这个ModuleA 项目作为我们的业务A组件。然后我们初始化pod,初始化podspec文件。

第三步:

我们创建一个空工程项目:ModuleB,这个ModuleB 项目作为我们的业务B组件。然后我们初始化pod,初始化podspec文件。

第四步:

我们创建一个空工程项目:ComponentMiddleware,这个项目就是我们上面所说的中间调度者。然后我们初始化pod,初始化podspec文件。

第五步:

我们创建一个空工程项目: ModuleACategory,这个工程是对应业务组件A的一个分类工程。然后我们初始化pod,初始化podspec文件。

第六步:

我们创建一个空工程项目: ModuleBCategory,这个工程是对应业务组件B的一个分类工程。然后我们初始化pod,初始化podspec文件。

好了,上面的主工程和两个业务组件工程,以及两个组件分类工程都已创建完毕,下面我们来讲解他们各个之间如何工作的。我就从主工程加载业务组件开始往下捋,顺藤摸瓜式的引出每个工程的用意。

第七步:

我们在主工程MainProject的Podfile中引入我们的业务组件B工程ModuleB,以及引入我们的ModuleB的分类工程:ModuleBCategory。然后我们pod install。这时已将这两个组件库引入到我们的主工程中了。

示例代码如下:

# Uncomment the next line to define a global platform for your project
platform :ios, '8.0'

source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/guangqiang-liu/GQSpec.git'

target 'GQComponentDemo' do
  
  pod 'ModuleB'
  pod 'ModuleBCategory'
end

然后我们在主工程中添加一个按钮事件,这个事件是点击 push 到业务组件B的 页面。

示例代码如下:

#import <ModuleBCategory/ComponentScheduler+ModuleB.h>

- (void)moduleB {
    UIViewController *VC = [[ComponentScheduler sharedInstance] ModuleB_viewControllerWithCallback:^(NSString *result) {
        NSLog(@"resultB: --- %@", result);
    }];
    [self.navigationController pushViewController:VC animated:YES];
}

第八步:

上面第七步中,我们用到了ModuleBCategory 这个分类工程。这个工程我们只对外暴露了两个文件。这两文件是上面的中间调度者的分类,也就是说是中间件的分类。我们先来看下这个分类文件的.h 和.m 实现。

.h

#import "ComponentScheduler.h"

@interface ComponentScheduler (ModuleB)

- (UIViewController *)ModuleB_viewControllerWithCallback:(void(^)(NSString *result))callback;

@end

.m

#import "ComponentScheduler+ModuleB.h"

@implementation ComponentScheduler (ModuleB)

- (UIViewController *)ModuleB_viewControllerWithCallback:(void(^)(NSString *result))callback {
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    params[@"callback"] = callback;
    return [self performTarget:@"ModuleB" action:@"viewController" params:params shouldCacheTarget:NO];
}
@end

我们发现这个分类实现非常的简单,就是对外暴露一个函数,然后执行[self performTarget:@"ModuleB" action:@"viewController" params:params shouldCacheTarget:NO]; ,并将执行的返回值返回出去。

这个分类的作用你可以理解为我们提前约定好Target的名字和Action的名字,因为这两个名字中间件组件中会用到。

上面的performTarget:action:params:shouldCacheTarget 函数是中间件提供的函数。因为ModuleBCategory 是 ComponentScheduler(中间件)的分类文件,所以可以调用到这个函数啦。

在ModuleBCategory 工程中需要引用到了中间件工程所以我们需要在ModuleBCategory 的Podfile文件中引用 中间件组件

示例代码如下:

# Uncomment the next line to define a global platform for your project
platform :ios, '8.0'

source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/guangqiang-liu/GQSpec.git'

target 'ModuleB-Category' do
  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
  # use_frameworks!

  # Pods for ModuleB-Category
  
  pod 'ComponentScheduler'

end

第九步:

因为上面第八步中引用到中间件工程,这里我们就来看下中间件工程到底做了什么工作。还记得上面第八步中,我们调用了一个中间件提供的函数:performTarget:action:params:shouldCacheTarget 吧,这个是中间件核心函数。

核心函数代码块如下:

image

还记得上面第八步中,我们调用这个函数传递的参数吧,我们在把调用代码拿过来看下

[self performTarget:@"ModuleB" action:@"viewController" params:params shouldCacheTarget:NO];

我们可以看到 TargetName 是我们传递的 ModuleBaction是我们传递的viewController,然后我们将 这两个参数传给了下面的函数:

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

我们来看下这两个参数的值具体是什么:

image

这个函数最终调用到苹果官方提供的函数:[target performSelector:action withObject:params];

看到 performSelector: withObject: 大家应该就比较熟悉了,iOS的消息传递机制。

[Target_ModuleB performSelector:Action_viewController withObject:params];

上面这行伪代码意思是: Target_ModuleB这个类 调用它的 Action_viewController: 方法,然后传递的参数为 params

细心的小伙伴们就会发现,我们没有看到过哪里有这个Target_ModuleB 类啊,更没有看到Target_ModuleB 调用它的 Action_viewController: 方法啊。

是的,这个Target_ModuleB 类和类的Action_viewController 方法就在第十步中讲解到。

第十步:

终于到了最后一步了,写的好艰辛,嗯,小伙们不要捉急,快了,快讲完了

细心的小伙们发现,我们上面讲的9步中,好像都没有提业务组件B的东西。是的,业务组件B除了提供组件B的业务功能外,业务组件B还需要为我们提供一个Target文件。

我们先来看下业务组件B的业务代码:

示例代码如下:

#import "ModuleBViewController.h"
#import "PageBViewController.h"

@interface ModuleBViewController ()

@end

@implementation ModuleBViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.title = @"我是模块B业务组件";
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(0, 0, 300, 100);
    btn.backgroundColor = [UIColor greenColor];
    btn.center = self.view.center;
    [btn setTitle:@"模块B业务功能组件" forState: UIControlStateNormal];
    [btn addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}

- (void)push {
    PageBViewController *VC = [[PageBViewController alloc] init];
    [self.navigationController pushViewController:VC animated:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

我们发现,业务组件B的业务代码也很简单,就是做一个push 跳转操作,从PageA 控制器跳转到 PageB 控制器。 这个没有什么好讲的

我们再来看上面提到的target文件

示例代码如下:

.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface Target_ModuleB : NSObject

- (UIViewController *)Action_viewController:(NSDictionary *)params;

@end

.m

#import "Target_ModuleB.h"
#import "ModuleBViewController.h"

@implementation Target_ModuleB

- (UIViewController *)Action_viewController:(NSDictionary *)params {
    ModuleBViewController *VC = [[ModuleBViewController alloc] init];
    return VC;
}

@end

从上面的实现文件中,我们可以看到,Target文件的作用也很简单,就是为我们提供导航跳转的目标控制器实例对象。这里的目标控制器实例就是业务组件B的ModuleBViewController 实例。

细心的小伙伴们发现,咦!我们在第九步中打印出来的targetaction 不就正是Target文件的Target_ModuleB Action_viewController:

上面我们只是串讲了业务组件B的一系列流程,业务组件A的用法和业务组件B的用法一样,如果后面再有业务组件C,D,都是一样的道理,就不再一一讲解了。

好了,现在小伙伴们应该看懂了这一连串的工作流程了吧,如果还没有看懂,那就重新看一遍咯。作者建议直接运行提供的示例Demo项目进行调试,这样便于理解各个组件之间的关系。

最后,我们再来看张组件化完整的架构图:

image

总结

上面我们讲解的只是简单的项目组件化架构的基础框架搭建,但是在真正的企业开发中,我们只搭建这样一个简单项目框架结构还远远不能满足需求的开发,我们还需要在项目框架中添枝加叶来满足现有需求。在上面提供的示例Demo中,我将电子发票业务组件独立成一个完整的工程,并结合了当下比较流行的MVVM设计模式和RAC数据绑定框架来实现电子发票模块的功能开发。如果有小伙们对 MVVM + RAC 实战开发感兴趣的,可以单独 install 电子发票工程查看,工程地址:iOS-MVVM-RAC

好了,又写到凌晨了,不早了,本篇教程到此就讲完了。下篇教程讲解如何使用MVVM+RAC进行实战开发。小伙伴们,感觉文章对你有帮忙,简书点个赞呗,组件化开源项目 iOS-Component-Pro 也帮忙点个 star 吧,先谢过。

掘金专栏文章详解

参考文献

更多文章

  • 作者React Native开源项目OneM地址(按照企业开发标准搭建框架完成开发的):https://github.com/guangqiang-liu/OneM:欢迎小伙伴们 star
  • 作者简书主页:包含60多篇RN开发相关的技术文章http://www.jianshu.com/u/023338566ca5 欢迎小伙伴们:多多关注多多点赞
  • React Native QQ技术交流群(600+ RN工程师):620792950 欢迎小伙伴进群交流学习
  • iOS QQ技术交流群:678441305 欢迎小伙伴进群交流学习

More Repositories

1

OneM

OneM是一款纯ReactNative打造的集杂志浏览、音乐播放、视频播放于一体的综合性App, 项目完全使用Redux状态管理,完全按照企业级架构标准搭建项目架构,项目中支持自定义多种UI组件和工具组件,支持iOS和Android双平台 ,墙裂推荐小伙伴们参考学习。
JavaScript
1,362
star
2

mpvue-meituan

基于mpvue 框架. Vue.js开发、搭建一套完整的美团外卖点餐小程序项目,以及小程序项目架构设计实践
JavaScript
518
star
3

react-navigation-demo

对导航组件react-navigation的使用讲解
JavaScript
78
star
4

iOS-MVVM-RAC

iOS MVVM开发模式 配合 RAC 信号绑定框架让开发更高效有趣
Objective-C
67
star
5

react-native-video-demo

reactNative中封装的仿爱奇艺视频播放器demo
JavaScript
57
star
6

react-native-toastAndLoading

封装一个基于iOS与Android双平台的Toast组件和加载Loading组件
JavaScript
46
star
7

react-native-gradientNavigationBarDemo

实现类似天猫首页的滑动导航颜色渐变效果组件
Objective-C
42
star
8

react-native-fetch-demo

自定义reactNative中的fetch网络请求工具类
JavaScript
38
star
9

react-native-shoppingCartDemo

封装仿京东APP购物车组件
JavaScript
34
star
10

CodePushDemo

RN项目接入CodePush实现热更新技术
Objective-C
32
star
11

react-native-categoryListDemo

仿天猫商品类目分类列表页面
JavaScript
28
star
12

AutoPackageScript

iOS、Android自动打包脚本文件
Shell
27
star
13

iOS-NotificationExtensionDemo

iOS 10 处理杀进程语音播放,串行语音播报问题,通知扩展类详解
Objective-C
27
star
14

OneM-API

总结出OneM开源项目中使用到的模块数据API
26
star
15

react-native-audio-demo

reactNative中封装的仿网易音乐播放器功能demo
JavaScript
24
star
16

react-native-routerFluxDemo

react-native-router-flux路由组件使用方式Demo
JavaScript
19
star
17

OneM-preview

纯ReactNative项目OneM中的页面效果图预览表
17
star
18

iOSHybridRNDemo

iOS原生与RN混合开发,入门Demo示例
Objective-C
17
star
19

redux-saga-todoListDemo

redux-saga框架版本的 todoListDemo, 适合初学者更好的理解 redux-saga 框架的使用
JavaScript
16
star
20

react-native-shareLogin

集成QQ微信分享登录功能Demo
Objective-C
15
star
21

react-dva-counter

入门dva框架的Counter Demo, 用来帮助同学们理解dva 框架的思想
JavaScript
15
star
22

iOS-Hybrid-RNDemo

iOS原生嵌套React Native实战开发,高级用法
JavaScript
14
star
23

iOS-Specs

iOS 组件化开发私有specs索引库
Ruby
13
star
24

react-native-storage-Demo

在react-native-storage组件的基础上封装storage 数据存储组件单利工具类
JavaScript
13
star
25

react-dva-todoList

dva版本的todoList项目,用来帮初学者更好的理解dva框架
JavaScript
12
star
26

redux-saga-counterApp

使用redux-saga框架搭建的 CounterApp Demo,帮助初学者更好的理解 redux-saga 框架
JavaScript
11
star
27

react-native-keyboardManager

解决在iOS平台下的,键盘遮挡文本框问题
Objective-C
7
star
28

react-native-gaussianBlurDemo

实现页面背景图片高斯模糊效果
Objective-C
7
star
29

react-native-ant-design-demo

reactNative中接入蚂蚁金服Ant-Design组件库
Objective-C
6
star
30

react-native-navigator-component-demo

reactNative中封装一套自定义的导航栏组件
JavaScript
5
star
31

react-native-ArrayTool

封装一个实用的数组操作工具类
JavaScript
5
star
32

react-native-commonStyleSheet

封装一个基于UI标准的公用样式表
JavaScript
5
star
33

react-native-iconfont

reactNative工程中使用iconFont,以及自定义font库
Objective-C
5
star
34

react-native-reduxDemo

学习理解redux框架之CounterDemo和TodoDemo
JavaScript
4
star
35

iOS-ModuleB-Category

iOS 组件化开发中,业务模块B的路由导航组件
Objective-C
3
star
36

iOS-Widget

iOS widget 基础组件
Objective-C
3
star
37

iOS-BaseView

iOS 基类 view 组件库
Objective-C
3
star
38

MarkdownSyntax

史上最全Markdown常用语法总结,学习Markdown看我就够了
3
star
39

iOS-Component-Middleware

iOS 组件化开发中各个独立模块组件间的调度中心管理器
Objective-C
3
star
40

react-native-styleSheet-demo

reactNative中封装的一套适配iOS和Android双平台的基类样式表组件
Objective-C
2
star
41

iOS-Modal

iOS modal 弹框 组件库
Objective-C
2
star
42

11.3-AutoreleasePool

11.3-autoreleasePool底层原理ARC,讲解示例Demo
Objective-C
2
star
43

iOS-PickerView

iOS picker 组件
Objective-C
2
star
44

iOS-IconFont

iOS iconFont 组件
Objective-C
2
star
45

06-BlockDemo

06-iOS中block底层原理,本质讲解示例Demo
C++
2
star
46

06.6-BlockDemo6

06.6-block循环引用,讲解示例Demo
C++
2
star
47

iOS-Module-Category

iOS 组件化开发中电子发票业务模块组件分类,用于进行调度器路由导航跳转
Objective-C
2
star
48

iOS-Form

iOS form 表单组件库
Objective-C
2
star
49

11.1-AutoreleasePool

11.1-autoreleasePool底层原理(iOS),讲解示例Demo
C++
2
star
50

iOS-moduleB

iOS 组件化开发实践中的业务模块B 组件
Objective-C
2
star
51

RAC-API-Demo

2
star
52

GQSafeKit

一个使用runtime实现的简单的工程safe框架
Objective-C
2
star
53

07.2-RunTimeSuper

07.2-Runtime super关键字,讲解示例Demo
C++
1
star
54

06.2-BlockDemo2

06.2-iOS中block的类型,讲解示例Demo
Objective-C
1
star
55

11-AutoreleasePool

11-ARC局部对象释放时机,讲解示例Demo
Objective-C
1
star
56

06.4-BlockDemo

06.4-block捕获对象类型的变量,讲解示例Demo
C++
1
star
57

08-Runloop

08-Runloop的本质,讲解示例Demo
Objective-C
1
star
58

08.2-RunloopThread

08.2-Runloop控制线程的生命周期,讲解示例Demo
Objective-C
1
star
59

07-RumtimeMethodCache

07-Runtime方法缓存,讲解示例Demo
Objective-C
1
star
60

09-GCD

09-多线程之GCD,讲解示例Demo
Objective-C
1
star
61

08.1-Runloop

08.1-Runloop的实际应用场景,讲解示例Demo
Objective-C
1
star
62

react-native-validator-demo

reactNative中封装的一套表单提交参数自动校验组件
1
star
63

10-NSTimer

10-内存管理中NSTimer常见问题,讲解示例Demo
Objective-C
1
star
64

03-KVO

03-iOS中KVO底层实现原理Demo讲解
Objective-C
1
star
65

04-KVC

04-iOS KVC底层本质,实现原理Demo讲解
Objective-C
1
star
66

07.3-RunTime-isMemberOfClass-isKindOfClass

07.3-RunTime中 isMemberOfClass,isKindOfClass的本质区别,讲解示例Demo
Objective-C
1
star
67

05-Category

05-iOS中Category底层原理,Demo讲解
C++
1
star
68

MarkDownImgs

Markdown 上需要访问的图片仓库
1
star
69

06.3-BlockDemo3

06.3-iOS中block的copy,讲解示例Demo
Objective-C
1
star
70

06.5-__blockDemo

06.5-block中__block本质,讲解示例Demo
C++
1
star
71

09.1-ThreadLock

09.1-多线程中的各种锁,讲解示例Demo
Objective-C
1
star
72

07.1-RunTimeMessageSend

07.1-Runtime消息机制,讲解示例Demo
C++
1
star
73

RN-Demo

本项目是作者公司的CRM试验性RN项目,由于CRM选择使用原生开发,顾此项目就没有发布上线,作者就将其开源出来供大家学习参考
1
star
74

10.1-__weak

10.1 __weak底层原理,讲解示例Demo
Objective-C
1
star
75

11.2-AutoreleasePool

11.2-autoreleasePool底层原理,讲解示例Demo
Objective-C
1
star
76

06.1-BlockDemo1

06.1-iOS中block的变量捕获,讲解示例Demo
C++
1
star