项目大了,编译慢了,开发效率低了,怎么办? 也许你已经知道了组件化,但项目迭代任务紧张,根本没有时间进行整体解耦,更害怕一下子改动太大导致的风险不可控,不敢大改,怎么办?
先别急着放弃,渐进式组件化了解一下
在实行组件化改造之前,我们对业内的一些技术文章及开源库进行调研之后,发现基本上千篇一律地都是基于路由这种方案作为通信引擎来实现组件化,重要的是组件化之前得先解耦原来的项目代码。很尴尬,我们没那么人力和时间来一下子做这么一大块事情。这时候我们是这样想的:
可不可以先不解耦?
这个问题问得好,可是...不先解耦,组件怎么单独以 app 运行呢?
新业务新 module 可以无耦合,总可以单独运行调试了吧?
组件需要登录怎么办?
需要跟其它组件通信怎么办?
用 URLScheme 来跨 app 通信总可以了吧?
页面跳转还行,虽然有个中转页面,仅在开发期间使用勉强也能接受
但如何获取服务?那可是要查找接口的实现类,跨 app 调用时行吗?
将需要调用的业务代码一起打包总可以了吧?
确实可以,但这些代码在哪里?主 app module 看一下?
难不成要跟主 app 一起打包?没解耦的那些 module 要全部编译才行哦
那...有没有这样一种方案:让我们可以立即就在新的业务上用组件化的形式进行开发(单组件 module 以 apk 的形式编译运行调试,并且可以与其它业务模块互相通信),享受到组件化编译速度快、调试效率高的好处,又不需要马上解耦项目,而是在迭代过程中利用偶尔出现的碎片空闲时间来一个一个慢慢地解耦呢?
好想法,这就是我们想要的:立即组件化开发 & 渐进式组件化改造
不过很遗憾,好像没有什么现成的方案可用。
那我们就自己设计一个吧!
正式由于项目代码耦合度高,解耦困难,而且迭代任务紧张,没办法单独抽出时间来解耦,导致组件化改造一直停留在口头上。
为了在不影响业务迭代业务开发的前提下也能用组件化的形式来进行开发,我们设计了一个支持立即组件化开发 & 渐进式组件化改造的框架:CC (已开源,点这里看源码)
以 i 百联为例(上海百联集团旗下的一个电商 App )来看一下组件化之前项目中存在的耦合情况:
最终项目的耦合状态是这个样子的:
为了能让大家看得清楚,图片上仅仅列出了有限的几个模块,但即使是这样,我们用一团乱麻来形容它也毫不为过。
我们的工程师一直是在这样的环境下进行业务迭代开发和 bug 修复,改代码要小心翼翼,生怕引起其它逻辑出 bug,最主要的是开发 /调试效率非常低:在使用了 maven 的情况下,在 15 吋高配的 macbook pro 上编译运行每次都要花 3-5 分钟,在 16GB 内存的 windows 台式机上更是动辄 10-15 分钟,严重影响开发效率,组件化势在必行。
但业务迭代排期时间非常紧张,测试资源不足,我们需要的是:立即组件化开发 & 渐进式组件化改造
在了解渐进式组件化改造之前,先来看看与之相对的非渐进式组件化改造
非渐进式的组件化方案要求我们必须在组件化初期立即对项目进行解耦,至少是我们需要被新业务调用到的组件需要解耦成组件,否则新业务组件脱离主 app 单独运行调试的时候无法正常工作。
在渐进式组件化的方案中,可以先不用解耦,只需要让单独运行的组件能够调用到主 App 中的功能即可。思路是这样的:
动画旁白:
IComponent
接口的实现类,对外暴露自身提供的服务,并且可以独立编译运行调试IComponent
接口实现类,对外暴露自身的服务,让会员积分组件能够跨 App 调用到这些组件IComponent
接口实现类,对外暴露自身的服务,让浏览记录组件能够跨 App 调用到商品详情组件IComponent
对应多个组件),等将来有时间了再继续拆分首先,CC 的核心通信引擎采用的不是路由方案,而是组件总线方案(路由 vs 组件总线)
CC 采有 2 套调用流程:App 内部组件调用和跨 App 调用。
框架在接收到组件调用请求时,优先查看当前 App 内是否有本次 CC 调用指定的组件
采用组件总线的方案,在 App 内部调用组件时,等效于直接调用IComponent.onCall(cc)
方法,将调用方设置的调用参数传递给组件,组件执行完之后将执行结果返回给调用方,这个过程中没有使用反射,执行效率高
在这个过程中,CC 完成了一次调用请求的转发:查找到组件对象,并将调用其 onCall 方法,将调用参数发送给它,并将组件执行的结果返回给调用方
悄悄地告诉你:CC 中自带 3 种 AOP 策略,例如动画中显示的CustomerInterceptors
就是其中之二:全局拦截器和针对本次 CC 调用的拦截器。定义一个拦截器也很简单:实现IGlobalCCInterceptor
接口即可
通过RemoteCCInterceptor
与另一个 App 的ComponentService
建立连接,将 CC 中的调用参数传递给ComponentService
,在ComponentService
中使用这些参数,发起一个 App 内部的 CC 调用,最终通过LocalCCInterceptor
调用到IComponent.onCall(cc)
。
组件的执行结果原路返回给调用方
在这个过程中,CC 完成了 2 次调用请求转发:
可以看出,跨 App 调用时:
IComponent.onCall(cc)
方法也是在LocalCCInterceptor
中调用,与在 App 内部调用时一样本文从我们在实际实施组件化过程中的一些思考入手,引入了渐进式组件化的概念,介绍了用 CC 来实现立即组件化开发 & 渐进式组件化改造的实施步骤。
并用动画的方式向大家展示了渐进式组件化与非渐进式组件化的区别以及支撑 CC 实现渐进式组件化的组件调用流程。
本文中的图片及动画取自上周末(6 月 9 日)在爱奇艺移动技术沙龙分享时的演讲稿,PPT 文档可在 CC 交流群(QQ 群:686844583)的群文件中下载,欢迎加群交流。
点击这里了解更多 CC 相关的内容
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.