iOS 组件化实践思考

组件化的应用背景和优势在此不再赘述,下面我们将从实践的角度,讨论一下如何应用组件化的思想,下面将以我自己的理解逐步展开,抛砖引玉。

哪些内容需要组件化

在我的理解中,一个项目可以拆分为以下几种组件:

  • 基础组件;

  • 功能组件;

  • 业务组件;

下面依次来解释几种组件的定义和规则。

基础组件

  • 基本配置

  • 常量;

  • 宏定义;

  • 分类

  • 各种系统类的扩展;

  • 网络

  • 对 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

}

}

}

可以想到,也会有很多的业务组件会使用到这个扩展。

功能组件

  • 控件

  • 弹幕;

  • 轮播;

  • 菜单;

  • 瀑布流;

  • 功能

  • 断点续传;

  • 音视频处理;

  • CUPImage 封装;

功能组件分为可见和不可见两种,可见的是控件,不可见的是功能。功能组件的作用顾名思义,就是实现了一个功能。

业务组件

业务组件,也就是业务的具体实现了,比如一个 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

其中 .widthSCREENWIDTH ,都在基础组件中,但基础组件中不仅仅是这些东西,如果依赖了基础组件,就需要导入基础组件中其他无用的代码,而且其他人使用轮播图组件,也需要导入基础组件。

因此,在功能组件中,不建议依赖基础组件,�上面的代码应该改成这样:


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 参数,通过中间件 NetWorkMWNetWork常量 两个基础组件组合,完成默认参数网络请求的封装。


// 基础组件 - 常量

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)

}

以上实际上是怎么样把多个组件组合使用起来,这种组合是确定的,还有一些是不确定的,例如有一个组件的状态改变了,我要让其他组件知道我的变化,但是我不知道都要告诉谁,怎么办?

眼珠一转,对外暴露状态变化,中间件在变化时发送通知。但是同时我想附带一个模型过去,通知的接收方怎样正确的使用这个模型呢?如果要使用模型,势必要和发送通知的业务组件产生耦合,怎么办?

以后再办,先埋个坑,这些场景我们会在以后再讲到。

组件分离的难点

组件分离的重点和难点也就是解耦,比如我们现在负责一个项目,其中的一个业务或者功能,希望实现组件化,但是它依赖于项目中的其他公共功能,该如何处理呢?这里提供两种思路:

  1. 拷代码,简单粗暴,摆脱依赖,对于一些不重要的工具方法,可以直接拷贝到内部来使用;

  2. 把组件依赖的代码先做一个 pod 库,然后依赖这个 pod 库;

上面讲到的是代码方面的依赖,还有一种情况是功能方面的依赖,比如我们有一个菜单,这个菜单涉及到网络图片的加载,那么怎样将这个菜单进行组件化呢?

  1. 使用 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 的依赖。

附加问题

以上的环节掌握了,应该可以尝试简单的组件化了,但是问题没完,还有哪些呢?

库的升级维护

随着项目的迭代,你负责的库升级了,其他的小伙伴们还在用上个版本的库,怎么办?

各种路径资源问题

我们在自己的库里使用了 imageNamedmainBundle,但是小伙伴把我们的库拖过去后,这些路径和我们不是一个路径,Assets.xcassets 跟我们也不是同一个 Assets.xcassets,怎么办?

这些问题你可以从这篇文章找到答案:你真的会用 CocoaPods 吗?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容