我所理解的组件化之路
为什么会有这篇文章呢?
和之前的同事"我是你爸爸"讨论了关于组件化的事,对我有很大的启发。在此特别感谢"我是你爸爸"。
最近写了关于组件二进制化的文章的文章,有点感触。
一些朋友来问我关于CocoaPods的问题提到了组件化。
自己一开始准备写《组件化之路》的博文的,但是后来发现我的理解是有偏差的。
以上,所以我想写一篇关于《我所理解的组件化之路》的博文来阐述自己的观点。
先提出一个新词,我自己想的。叫做“CocoaPods化”或叫做“library化”
什么叫做CocoaPods化?
CocoaPods化也就是我们公司正在做的。随着业务的扩展,有了多个App,有了多个Team,我们希望把一些代码重用。使用CocoaPods把他们做成library是个很好的选择。也可以说是CocoaPods化之路。
1.和业务无关。
开始做这件事的时候,我们会容易的想要把那些Util、Category、JSBrige等等这些和业务无关的源码搞在一起做成一个一个CocoaPods库。它们变成了YTXUtilCategory、YTXWebViewJavaScriptBridge、YTXNibBrige、YTXAnimations这些库。
2.弱业务
接下来,进一步地我们会把那些比如网络请求、Server配置、行情图、行情Socket等等这些弱业务的源码搞在一起。她们变成了YTXRequest、YTXServerId、YTXChart、YTXChartSocket、YTXChatUI等等。
为什么说是弱业务呢,稍微分析下。比如YTXRequest、YTXChartSocket、YTXServerId在公司内部各个App,各个Team之间是通用的,在各个业务组件之间可以重用和组合使用;又带着鲜明的公司特色,没法直接开源了就能让其他开发者使用。
如果只做到了前2步,我觉得不能称之为组件化。只能叫做CocoaPods化或Library化。
3.业务
这一步,到目前来说没有做。所以没法举我自己实际的例子。
比如拿美团App做例子来说。一条业务线是外卖,一条业务线是电影。分别由2个Team维护开发(技术,产品,测试等)。有各自的KPI。这两条业务线是自洽的,是分治的。
外卖是一个业务组件,电影也是一个业务组件。里面包含了各种内容,各种依赖。外卖可以手写autolayout,电影可以用storyboard。外卖可以用mvc,电影可以mvvm。想怎么搞就怎么搞。他们两个就像独立的App一样。
有不少朋友包括我自己之前,认为做了前2步就是组件化了。只有真正做到了第3步,并且完善了相关架构,我才认为能称之为组件化。
那么我们来看看真正的组件化应该包含什么,什么情况适合组件化。业界内部的讨论已经有很多了,我来列举下我自己的看法。
画一个图:
适合的情况
- 业务上要分治。
- Team规模大,30人+。
- 业务越来越多,越来越大。
如果不符合这些情况,我认为做组件化没有意义。因为性价比太低。
有一种情况表面上都符合上面列的条件,但实际上不适合组件化:例如我们公司。虽然有好几个iOS Team,虽然总人数上超过了30人,但每一个Team都只有6~10人。每个Team各自维护各自的一个App,各个App业务上没有交集,只公用1和2步的CocoaPods库。就算有交集,做相同的业务,也不打算公用或重用这部分代码(内部有竞争关系)。
我们公司这种情况就像是拆分成了好几个无关的小公司,大家都用了github上的一些CocoaPods库一样。
还好早期推了第1步和第2步,避免了每个Team之间都去造差不多功能的轮子,而能把精力尽量集中在各自的业务上,避免了一些资源浪费。
我认为需要包含什么(不分先后顺序)
- App生命周期及事件如何下发给业务组件。
- 业务组件之间没有依赖关系,需要解耦。
- 解决组件化页面跳转的问题。
- 解决业务组件之间通信的问题。
- 解决如何划分抽象业务组件、基础功能组件(业务无关)和弱业务组件。
- 统一的网络服务,本地存储方式等。
- 去Model化。
- 如何披露接口信息,调用方式,参数等等。
- 明确组件的生命周期。
- 提供二进制化方案。
- 组件的subspec。
- 版本规范。
- 持续集成。
- 代码准入制度。
- 统一的命名规范。
- 集成调试。
- 代码维护。
所以我们得出的结论是:不轻易组件化。而是统筹规划好以上所有的内容。可以不用一步就位全部做好,但要预先想好每一步的解决方案;能够承上启下。
如果你要问我说哪一步比较重要,我觉得都挺重要的。要结合自己的实际情况,去排一个优先级。
App生命周期及事件如何下发给业务组件
例如:applicationDidEnterBackground,didRegisterUserNotificationSettings,didReceiveRemoteNotification等等。通过注册方式,App向注册的业务组件中的协议发送消息。
业务组件之间没有依赖关系,需要解耦
通过依赖协议,或依赖下沉等方式解耦。准确拆分业务组件,弱业务组件,基础功能组件。保证单一原则、DRY 原则等。
解决组件化页面跳转的问题
各种router。比如MGJRouter。我不建议是淡出使用URL传参。理由是可以传参的对象受限制。
我们自己有一套叫GOTO的东西。使用分类。唯一的问题,你需要知道你要跳转页面的去model化参数是什么,代表该页面的枚举是什么,目前没法注册。
解决业务组件之间通信的问题
组件间需要相互调用,监听回调。不是说不能相互依赖么?对,可以通过依赖协议或中间件(依赖下沉)等方式解决这个问题。比如CTMediator。CTMediator应该是属于依赖下沉的方式。
解决如何划分抽象业务组件、基础功能组件(业务无关)和弱业务组件
这个得要从各自的实际情况出发。但有几个原则可以借鉴: