内容简介:组件化的应用背景和优势在此不再赘述,下面我们将从实践的角度,讨论一下如何应用组件化的思想,下面将以我自己的理解逐步展开,抛砖引玉。在我的理解中,一个项目可以拆分为以下几种组件:下面依次来解释几种组件的定义和规则。
组件化的应用背景和优势在此不再赘述,下面我们将从实践的角度,讨论一下如何应用组件化的思想,下面将以我自己的理解逐步展开,抛砖引玉。
哪些内容需要组件化
在我的理解中,一个项目可以拆分为以下几种组件:
-
基础组件;
-
功能组件;
-
业务组件;
下面依次来解释几种组件的定义和规则。
基础组件
基本配置
-
常量;
-
宏定义;
分类
-
各种系统类的扩展;
网络
-
对 AFN 的封装;
-
对 SDWebImage 的封装;
工具类
-
文件处理;
-
设备信息;
-
时间日期处理;
基础组件的含义就是最基础的东西,每个业务组件都有可能会使用到,基础组件需要抽取的应该是类似上面的代码,举例来说,比如我们定义了一个常量,表示接口的根路径:
let BASEMIRRORURL = "http://rest.mirror.xxxx.com/ios"
那么这个常量在 Home,List,Detail 都有可能会被引用,因此我们将这种最底层的,最下一层的东西归类到基础组件。
又比如分类和扩展,我们给 UIView 的扩展定义一个计算属性:
extension UIView { var height { set { self.frame.size.height = newValue } get { return self.frame.size.height } } }
可以想到,也会有很多的业务组件会使用到这个扩展。
功能组件
控件
-
弹幕;
-
轮播;
-
菜单;
-
瀑布流;
功能
-
断点续传;
-
音视频处理;
-
GPUImage 封装;
功能组件分为可见和不可见两种,可见的是控件,不可见的是功能。功能组件的作用顾名思义,就是实现了一个功能。
业务组件
业务组件,也就是业务的具体实现了,比如一个 App 的骨架如下:
-
首页;
-
发现;
-
我的;
首页下又分为这样:
-
侧滑菜单;
-
Banner;
-
热门;
这里的每个部分,都可以称为业务组件。
三种组件的关系
基础组件规则
基础组件和基础组件之间不应该产生依赖,比如我们使用网络请求组件,希望根路径是一个默认参数,但可以对外暴露和修改,像下面这样:
class NetWork { func request(baseUrl: String = BASEMIRRORURL, path: String, param: [String:Any]) { } } NetWork.request(path: "/g/login.server", param: param)
这时,NetWork 就依赖了 常量 这个基础组件,我们如果使用 NetWork 基础组件,还需要导入 常量 这个基础组件,这是 不应该 的。
但为了代码的简洁性,这样的封装又是必要的,那么应该怎么做呢?这个问题我们下面会讲到。
功能组件规则
功能组件和基础组件之间不应该产生依赖,比如我们做轮播图,会用到 UIView 的扩展和 常量 ,像下面这样:
imageView.width = SCREENWIDTH
其中 .width 和 SCREENWIDTH ,都在基础组件中,但基础组件中不仅仅是这些东西,如果依赖了基础组件,就需要导入基础组件中其他无用的代码,而且其他人使用轮播图组件,也需要导入基础组件。
因此,在功能组件中,不建议依赖基础组件,上面的代码应该改成这样:
imageView.frame.size.width = UIScreen.main.bounds.size.width
或者直接复制代码,将需要的基础组件的功能,复制到功能组件当中。
同基础组件一样,功能组件和功能组件也不应该产生依赖,道理是一样的,我们使用一个功能,不应该将另一个功能也导入进来。
业务组件规则
基础组件和功能组件都是为业务服务的,因此业务组件可以依赖于基础组件和功能组件,快速的实现业务,但是 业务组件和业务组件之间不应该产生依赖。
比如这样一条业务线,我们要求 发现 这个业务组件,点击一条视频,跳转到 视频播放器:
func pushToPlayerVC(model: VideoModel) { let vc = PlayerVC(videoModel: model) navigationVC.push(vc) }
这时 发现 就对 视频播放器产生了依赖,如果将 发现 进行组件化进行剥离,能行吗?不行。
其实这个问题和网络请求使用默认参数封装一样,是组件与组件之间的通讯问题,当然,这个问题我们下面会讲到,现在再提一下是为了一会儿往下写的时候忘了填坑 ...
每个组件存在的形式
-
组件内部;
-
组件外部;
-
组件测试;
组件内部
组件的内部应该使用 设计模式 划分文件夹的结构,例如 MVVM 结构:
---- PlayerView -- View -- Model -- ViewModel
组件外部
组件的外部应该是一个远程私有 pod 库,使用 CocoaPods 进行管理。
组件测试
单独的测试工程。
怎样集成各个组件
组件的集成应该像上面的图一样,基础组件和功能组件互不依赖,制作远程 pod 私有库,业务组件依赖于这些 pod 私有库开发,同样制作成远程 pod 私有库,壳工程依赖于 CocoaPods 管理这些私有库,完成整个项目。
当然还有另外的方式,比如将壳工程作为主工程,组件创建为子工程,这方式的缺点是子工程可以修改,缺少约束性,目录结构也比较凌乱。
还有将组件制作为 FrameWork,壳工程中导入一个个 FrameWork 库,这种方式个人感觉比上一种好一些,但是在物理上,组件和壳还是没能做到分离。
因此,我个人还是更倾向于 pod 库的形式。
组件之间的通讯
-
对外公开 API 接口;
-
通过中间件的中转;
上面我们有两个遗留的问题,归纳为组件之间的通讯问题,下面就通过这两个问题,讨论一下组件之间的通讯。
网络请求默认参数
下面的思路就是暴露出 baseUrl 参数,通过中间件 NetWorkMW 将 NetWork 和 常量 两个基础组件组合,完成默认参数网络请求的封装。
// 基础组件 - 常量 let BASEMIRRORURL = "http://rest.mirror.xxxx.com/ios" // 基础组件 - 网络请求 class NetWork { func request(baseUrl: String, path: String, param: [String:Any]) { } } //壳工程 - 网络请求中间件 class NetWorkMW { func request(baseUrl: String = BASEMIRRORURL, path: String, param: [String:Any]) { NetWork.request(baseUrl: baseUrl, path: path, param: param) } } NetWorkMW.request(path: "/g/login.server", param: param)
发现跳转视频播放
这个思路是使用代理,对外暴露点击事件,通过中间件,导入 视频播放 业务组件,topVC 基础组件,完成向 视频播放 的跳转:
// 业务组件 - 发现 func pushToPlayerVC(model: VideoModel) { delegate?.pushToPlayerVC?(videoModel: model) } // 中间件 - 发现 func pushToPlayerVC(model: VideoModel) { let vc = PlayerVC(videoModel: model) topVC.navigationVC.push(vc) }
以上实际上是怎么样把多个组件组合使用起来,这种组合是确定的,还有一些是不确定的,例如有一个组件的状态改变了,我要让其他组件知道我的变化,但是我不知道都要告诉谁,怎么办?
眼珠一转,对外暴露状态变化,中间件在变化时发送通知。但是同时我想附带一个模型过去,通知的接收方怎样正确的使用这个模型呢?如果要使用模型,势必要和发送通知的业务组件产生耦合,怎么办?
以后再办,先埋个坑,这些场景我们会在以后再讲到。
组件分离的难点
组件分离的重点和难点也就是解耦,比如我们现在负责一个项目,其中的一个业务或者功能,希望实现组件化,但是它依赖于项目中的其他公共功能,该如何处理呢?这里提供两种思路:
-
拷代码,简单粗暴,摆脱依赖,对于一些不重要的 工具 方法,可以直接拷贝到内部来使用;
-
把组件依赖的代码先做一个 pod 库,然后依赖这个 pod 库;
上面讲到的是代码方面的依赖,还有一种情况是功能方面的依赖,比如我们有一个菜单,这个菜单涉及到网络图片的加载,那么怎样将这个菜单进行组件化呢?
使用 Block 或者代理,将网络图片加载这部分的职责交给外部控制;
举例来说,像下面这样:
// 业务组件 - 菜单 self.imageView.sd_setImage(with: url, completed: completed)
那么如果现在将它组件化,这个组件就要依赖于 SDWebImage,我们应该修改成这样:
// 业务组件 - 菜单 setImage?(for: imageView, completed: ImageLoadCompletedBlock) // 中间件 - 菜单 menu.setImage = { (imageView, completed) in imageView.sd_setImage(with: url, completed: completed) }
现在菜单就摆脱了对 SDWebImage 的依赖。
作者:薛定諤
链接:https://juejin.im/post/59f2cc5d6fb9a04525775bce
以上所述就是小编给大家介绍的《iOS 组件化实践思考》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 组件化之路—集成组件SDK
- Android组件化入门:一步步搭建组件化架构
- Android快速开发框架,基础库,样式库,组件化,组件集成
- Android组件化方案及组件消息总线modular-event实战
- 组件化实践
- 组件化架构漫谈
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构与算法:Python语言描述
裘宗燕 / 机械工业出版社 / 2016-1 / CNY 45.00
本书基于Python语言介绍了数据结构与算法的基本知识,主要内容包括抽象数据类型和Python面向对象程序设计、线性表、字符串、栈和队列、二叉树和树、集合、排序以及算法的基本知识。本书延续问题求解的思路,从解决问题的目标来组织教学内容,注重理论与实践的并用。一起来看看 《数据结构与算法:Python语言描述》 这本书的介绍吧!