Swift组件化方案探究和实践

介绍几大组件化方案, 使用 Target-Action 设计的的 CTMdeiator 拆分工程

Posted by poos on October 10, 2018

iOS 组件化的探究

为了更好的进行项目管理、测试和开发,工程会有了组件化的需求。那么如何使组件化之间解耦、如何方便的调用、如何在现有工程改造组件化。这就到了如何开展组件化的讨论中了。

本文的背景是项目已经上线一年时间。现有的工程依赖比较复杂,代码划分不明确,且编译时间较长。那么本项目需求的组件化方案就有x以下要求:

  1. 可持续的拆分组件
  2. 解耦,在子工程可以编译子功能
  3. 调用方便
  4. 结构清晰,易于推广
  5. 尽可能轻量
  6. 侵入性小
  7. 易于扩展功能,例如统计等

介绍一下常见的组件化方案的原理

摘自halfrost/Halfrost-Field/blob

1. URLRoute注册方案的优缺点

首先URLRoute也许是借鉴前端Router和系统App内跳转的方式想出来的方法。它通过URL来请求资源。不管是H5,RN,Weex,iOS界面或者组件请求资源的方式就都统一了。URL里面也会带上参数,这样调用什么界面或者组件都可以。所以这种方式是最容易,也是最先可以想到的。

URLRoute的优点很多,最大的优点就是服务器可以动态的控制页面跳转,可以统一处理页面出问题之后的错误处理,可以统一三端,iOS,Android,H5 / RN / Weex 的请求方式。

但是这种方式也需要看不同公司的需求。如果公司里面已经完成了服务器端动态下发的脚手架工具,前端也完成了Native端如果出现错误了,可以随时替换相同业务界面的需求,那么这个时候可能选择URLRoute的几率会更大。

但是如果公司里面H5没有做相关出现问题后能替换的界面,H5开发人员觉得这是给他们增添负担。如果公司也没有完成服务器动态下发路由规则的那套系统,那么公司可能就不会采用URLRoute的方式。因为URLRoute带来的少量动态性,公司是可以用JSPatch来做到。线上出现bug了,可以立即用JSPatch修掉,而不采用URLRoute去做。

所以选择URLRoute这种方案,也要看公司的发展情况和人员分配,技术选型方面。

URLRoute方案也是存在一些缺点的,首先URL的map规则是需要注册的,它们会在load方法里面写。写在load方法里面是会影响App启动速度的。

其次是大量的硬编码。URL链接里面关于组件和页面的名字都是硬编码,参数也都是硬编码。而且每个URL参数字段都必须要一个文档进行维护,这个对于业务开发人员也是一个负担。而且URL短连接散落在整个App四处,维护起来实在有点麻烦,虽然蘑菇街想到了用宏统一管理这些链接,但是还是解决不了硬编码的问题。

真正一个好的路由是在无形当中服务整个App的,是一个无感知的过程,从这一点来说,略有点缺失。

最后一个缺点是,对于传递NSObject的参数,URL是不够友好的,它最多是传递一个字典。=

URLRoute注册,组件通过向 Manager中心注册,通过 url 形式调用其他组件

2. Protocol-Class注册方案的优缺点

Protocol-Class方案的优点,这个方案没有硬编码。

Protocol-Class方案也是存在一些缺点的,每个Protocol都要向ModuleManager进行注册。

这种方案ModuleEntry是同时需要依赖ModuleManager和组件里面的页面或者组件两者的。当然ModuleEntry也是会依赖ModuleEntryProtocol的,但是这个依赖是可以去掉的,比如用Runtime的方法NSProtocolFromString,加上硬编码是可以去掉对Protocol的依赖的。但是考虑到硬编码的方式对出现bug,后期维护都是不友好的,所以对Protocol的依赖还是不要去除。

最后一个缺点是组件方法的调用是分散在各处的,没有统一的入口,也就没法做组件不存在时或者出现错误时的统一处理。

Protocol-Class注册,组件通过向 Manager中心注册已经遵守协议,其他组件即可调自己

3. Target-Action方案的优缺点

Target-Action方案的优点,充分的利用Runtime的特性,无需注册这一步。Target-Action方案只有存在组件依赖Mediator这一层依赖关系。在Mediator中维护针对Mediator的Category,每个category对应一个Target,Categroy中的方法对应Action场景。Target-Action方案也统一了所有组件间调用入口。

Target-Action方案也能有一定的安全保证,它对url中进行Native前缀进行验证。

Target-Action方案的缺点,Target_Action在Category中将常规参数打包成字典,在Target处再把字典拆包成常规参数,这就造成了一部分的硬编码。

Target-Action,依靠 runtime 的 taget 调用 action,传入字典类型的param

通过三大方案的对比,我选择了轻量级的Target-Action方案。

之所以选择这个方案,有一点是因为我们需要在开发过程中 逐步迁移 实现组件化。另外使用Target-Action充分利用 OC/Swift 语言的特性。入乡随俗嘛。

简单研究之后,我写下了这个Demo poos/BlogDemo/tree/master/testCTMdeiator

Demo实现了 主工程调用组件组件调用主公程。实现了组件代码 插入即可运行移除之后主公程可运行其他业务

确定组件化方案

  1. 使用CTMediator,用 Jump 和 Action 来路由跳转和组件之间的调用。
  2. 主工程维护一个 CTMediator 分类,在分类中实现的组件都可以在主工程调用。
  3. 各个分类组件通过 pod 本地库的方式导入主工程。

开展组件化工作

先列个提纲,一步一实施。

1. 收纳整理主公程

  • 分类;

  • 公用组件;

  • 公用 UI及样式;

  • 公用第三方;

  • 项目基本目录修改(添加不同环境的 Scheme);

2. 主工程封装 pod 仓库

  • baseProject 基础仓库,所有公共资源和项目配置。原则:用tag标记稳定版本;可以用稳定版本开发新功能。

3. 主工程的分支管理

  • develop 开发分支,完整项目的开发分支。

  • test 测试分支,所有移交测试的版本,测试完成即可并入master。

  • master 主分支,只存放完整项目稳定的版本的分支,tag上线版本。

4. 其他子模块

  • xxxxx 小模块仓库等,pod 导入 baseProject 开发的模块pod

公共组件

基本上到上述第二步之后就可以很好的进行项目组件管理了,下一步对一些值得优化的公共组件进行优化。

  • 项目 Theme 设计

  • Navigator 页面跳转(可选)

  • 弹窗统一管理 设计

  • Review控制 设计

  • 通知统一管理 设计

  • 通用链接,调用系统分享等与iOS系统对接的设计

  • 测试环境下的专用配置,例如不上传统计,例如动态修改UI

end

通过这些优化项目会清晰,可控很多,在开发间隙慢慢迭代实施中…

一些好的资料