iOS 组件化实践
一、前言
关于为什么组件化,组件化都有哪些好处,我就不细说了,说说我的一点积累,会有很多不足,多多包涵。
- 并不是所有工程都适合组件化,如果你的工程规模不大的或单人开发,都不建议组件化,反而降低你的开发效率。工程规模大,团队开发的,组件化确实助力很大。
- 组件的拆分,不是所有页面都要做成组件的,一个工程有200多个页面,不可能有这么多的组件,只是某一个大功能会成为一个组件,比如首页组件,个人中心组件,某个功能的二级页面,基础组件(网络请求等 各种封装),如果是购物app,购物车一定是个组件。一个组件内肯定有多个页面和功能的,这个根据自己公司的业务模块来进行合理的划分即可。
- 组件是一个单独工程,在没有任何交互的情况下可以独立运行,但这是理想化。一个工程一定会有网络请求,会有一些和其他组件工程共用的类或文件。这些东西不能每个组件都写一遍,所以要有一个基础组件存放网络请求,公共类,图片等。所以组件单独运行实际情况是:组件+基础组件 才能运行
- 组件化一般是有一个主工程(壳子)和很多子工程组成,子工程的管理可以使用Workspace ,Workspace是xcode 自带的一种工程方式,多个工程存放在一起,一个Workspace管理多个子工程,这简直就是为组件化而设计的。
- 路由或中间件,没接触组件化的同学很多都认为路由或中间件就是组件化 或者是组件化的最重要的,其实路由或中间件只是组件化的一个跳转通信的工具,我觉得相比其他方面路由或中间件的地位很低,很多大神都写过各种思路的路由或中间件,我们选择个上手难度低的 MGJRouter,大家组件化成功后,可以更换其他路由或中间件,试试不同的通信思路。
二、创建Workspace
- 成功的创建
TestApp
TestAppModule
在github或码云上创建工程 并clone本地工程,或本地创建在上传。组件都是私有库的,不对外暴露,我这个是demo都是公开库了

TestAppModule 为各组件集合的工程 (Workspace)
TestApp 为主要工程(俗称壳子)最后所有的组件会在这个工程进行联调,并打包。
-
先从TestAppModule 组件的工程 开始
创建Workspace 名为 TestAppModule
42EB8AD3-E9D5-422E-9B10-A04D5224C176.png
存储在桌面的TestAppModule 文件夹里
在桌面的TestAppModule文件夹下创建多个项目工程 例:TestA TestB...

打开TestAppModule.xcworkspace,点击左下角+号 添加TestA等工程到xcworkspace

要选择工程的xcodepro 添加进来

最终是这样的

三、配置组件工程
-
组件化大家的印象中,单个组件是可以独立运行的,互不干扰,但是有的公共方法如 网络请求,共用的宏,图片等,如何处置。这时候就需要一个Basis组件来放置,每个组件都可能使用的公用类都要放在Basis组件里,Basis组件也负责所有pods第三方的引用,所以每次运行组件并不是单独的,应该是:TestA+TestBasis 才能运行。
-
TestBasis工程里 举例 如下配置,一些公共类不用细说,有变动的是图片读取,因为图片会跨组件读取(跨工程),正常图片读取 [UIImage imageNamed:@""]是不起作用的,这里写了个NSBundle的分类 ,后面会有调用
A8442080-EACE-4F9F-83F3-41ABE4F802D6.png
TestBasisHeader 是对暴露的类,里面包括所有公共类,第三方,路由URL,图片路径,接口文件等。test工程 因为引用的少我都写在一起了。
03898007-288D-43BE-8D2D-674515633D62.png
TestA 等其他工程里如下配置

-
podspec文件
本地工程创建podspec文件
cd到TestAppModule文件夹目录 pod spec create 工程名
和工程 xcodepro 一样添加进来
815ECA25-F3D4-4898-996A-B12C287CC98B.png
这时候可以先往github上传一次,因为下面我们配置Podfile和podspec文件 里面都要写github地址
podspec 配置十分重要,如果我们要集成第三方
重点:Podfile 文件进行配置 ,podspec也要改动
podspec里面的东西比较多 我只截取一部分,更多去工程TestAppModule
看

Podfile 文件

配置好了 我们pod install ,这个步骤可能会有多次的不成功,因为Podfile 文件和 podspec文件 都要写对
再次重申:Podfile 文件进行配置引用第三方 ,podspec也要对应改动
成功后下面打开工程先运行TestA 看看TestBasis与其他工程之间是否关联上
TestA 引用TestBasis 工程里的TestBasisHeader.h类

看到log 打印了,证明我们的组件+基础组件的模式成功了
这里要注意一点 我们在配置podspec文件的时候指定了每个组件最后到壳子工程上的文件,只有在Classses文件下的类才能集成进壳子

TestA 为例Classses文件 下的类才才能被集成的壳子内,其他的不行,但是每个组件单独运行时没有区别,所以ViewController可以引用TestBasisHeader.h,并使用TestBasis的方法。但无法集成到壳子工程内。

Classses文件 的pch 要解释一下

这个文件用不用都可以。pch文件在这的作用就是解耦,可以看到我把TestBasisHeader.h 文件放到这里,TestA工程内都可以使用了。这是优点,弊端也很明显引用TestBasisHeader.h 相当于大量的头文件和宏定义放到pch里边,导致编译时间过长。到后期壳子编译会很慢。
如果 你的某个组件没有使用pch,在组件里podspec 文件,要把对应的 prefix_header_file 注释掉。否则主工程pods会报错
我先在各工程中简单的布局,并在TestARoute.m中把路由注册上。

TestBRoute TestCRoute 都一样上
这时候我们可能会添加新的文件,类,或图片,需要git上 save 并push,

-
如果TestBasis 创建新的文件,并且别的组件需要使用这个新的文件,会引用报错,在save 并push之后 要pod install,别的组件才能使用。pod install这个操作在组件化中很频繁。
如果之后运行 还出现引用报错,未必是真的。可以(command+shift+k ) clean一下。clean这个操作 在组件化中经常用到,当我们添加一个新的文件,类,或图片在pod install 后都可以 clean一下再运行。 -
虽然我们把路由都配置完了,但是组件这个工程路由是无法进行跳转的。下面我们进行主工程(壳子)的配置。
四、主工程的联调
-
TestApp 是我们的主app工程,最后所有的组件会集成到这个工程进行联调,并打包。但并不是所有的功能都要设计成组件的,如TabBarController要显示 TestA TestB TestC,如果TabBarController也是组件,就要引用TestA TestB TestC,说好解耦呢,所以TabBarController,还有推送相关,一些第三方初始化,只能写在主工程,
下面我给主工程 集成组件
Podfile文件
52266469-761B-453D-A137-7FF0F1BFE8D5.png
编写完毕 pod install
-
这个很大几率会不成功,因为组件工程的Podfile文件 和podspec 里面如果有不符合就会报错,比如我就遇到了 我的组件工程的podspec文件 只有testA有pch文件 但是testC工程编写了c.prefix_header_file = 'TestC/TestC/Classes/TestC.pch''TestC/TestC/Classes/TestC.pch',但实际我的工程里没有这个TestC.pch文件,就报错了,把这行注释掉。重新pod install 成功
F3B95490-B05C-4E94-B619-A95B7427E5A8.png
我可以看下内部结构
E572AF35-596B-40AD-B7DA-40A95B191A72.png
上面讲过 我们的组件是在集成了每个工程的 Classes文件下的所有文件,所看起来以非常整洁。
接下来我们创建TabBarController 并引用组件的类最后运行起来
modulegif.gif
五、最后说下注意事项:
- 如在组件内新建文件夹 、类、图片等,确保不出错之后,一定要git提交,这时候运行主工程会报错或加载不到 ,主工程要pod install ,如果是图片等资源文件还要clean,运行成功好也要及时的提交git,因为组件化一般都是多人团队开发,git 操作冲突了,会很伤神。
- 加载资源文件,如图片等原来方法都会失效,需要路径加载。
- 有的组件未必使用基础组件内的某个功能,我的例子却一起引用了,这个解决很灵活可单独引用,也可在pod中操作单独集成,也可把基础组件再次拆分多个组件,根据业务模块来进行合理的划分即可。
工程地址
TestAppModule 组件工程
TestApp 主工程