项目 组件化

背景

最近公司主App3.0大升级 , 基本框架及大部分功能会参照另一个已有App, 为了方便升级以及后续两个App的开发 , 经过开发与产品讨论, 决定将已有App进行组件化

方案

组件化其实并不是一件全程明朗的事情, 在最开始定下使用cocoapods作为组件化的工具时, 开发只能根据目前App的功能点及功能间相关逻辑进行了一个简单的分类 , 并且按照这个分类大致分为3个阶段进行提测


image.png
image.png


组件化操作(参考之前写过的工具类组件化步骤, 可以跳过)

  1. 创建一个测试的iOS工程


    image.png
  2. 在该目录下创建一个 文件夹MeishaPod


    image.png
  3. 用命令行cd入MeishaPod中 并执行命令pod lib create MeishaTools

    image.png

    得到该文件
    image.png

  4. 把自已弄好的库扔进这个弄好的容器中 ,Classes中的replace文件可以删除掉


    image.png
  5. 在命令行中进入Example目录下pod install;然后打开Example中的工程 ,发现库已经全部进来了,并且是跟刚刚复制进Classes中的文件是一个源的。(既修改这里会改到Classes中的文件)

    image.png

  6. 修改这个库中的信息->打开MieshaTools.podspec。


    image.png
  7. 要提交上gitlab上面,在我们写的MeishaTools下面开始进行git操作

git init
git add .
git commit -m '描述'
git remote add origin https://gitlab.meishakeji.com/daniel/meishatools.git
git push origin master
image.png
  1. 接下来进入打tag环节,为什么要打tag呢?因为cocoapods要找到你这个库必须是通过tag来找的,并且在MieshaTools.podspec中的信息中要更新你的tag;如果不写,cocoapods发布你的库时候会找不到你在git上的库。
    可以这么理解 MieshaTools.podspec文件中的s.version就是 cocoapods要发布的你的库的git上面的版本 一一对应
git tag 1.0.3
git push --tag
image.png
  1. 这时候我们要上传到cocoapods了;用命令行进入MeishaTools,然后执行命令pod lib lint --verbose --use-libraries --allow-warnings
  • --verbose是查看打印的日志信息
  • --use-libraries 如果库中使用到framework就需要加上
  • --allow-warnings 是忽略警告
  1. 如果显示passed validation 就是证明编译通过 那么就可以执行发布了 执行命令 pod trunk push MeishaTools.podspec --verbose --use-libraries --allow-warnings
    提交成功后那么这个库就存在cocoapod上面了

  2. 使用这个库 要cd进iOS测试工程根目录中pod init 然后在podfile中导入这个库
    然后继续再执行命令pod install就可以再我们工程中使用这个基础工具类的库了

    image.png

image.png


组件化过程

第一阶段

  1. 根据预定目标 , 首先进行项目主框架的组件化 , 这一过程涉及三个点: 1. 项目的基础工具类 2. 项目的基础UI类 3.项目的框架结构类为了增加组件的通用性, 把工具类和基础UI类另外抽成了两个组件
    image.png

    在这一过程中, 由于视频上传和视频转场工具涉及到FFmpeg, 在进行组件化时, 这两部分暂时没有封进组件中(此过程相对比较顺利, 因为底层组件及控件一般不涉及业务, 组件化也就简单得多)
  2. 学生列表模块/登录模块/微主页模块的组件化, 这三个模块除了登录模块为了迎合两个App不同的弹窗逻辑, 做了较大改动, 另外的两个模块则十分顺利的完成了

第二阶段

  1. 营期相册/班级课表/班级空间 这三个模块组件化时, 因为模块内部没有与其他模块的关联逻辑, 也没有跳转逻辑, 因为组件化十分快速顺利
  2. 在班级动态组件化时, 由于第一阶段的视频上传模块没有完成, 因此大部分时都在处理FFmpeg及视频上传模块的组件化
  3. 成长记录的数据结构大部分是跟其他组件相关联, 并且有些处理逻辑也和对应组件有联动, 因此把成长记录的组件化放到最后, 等所有的其他模块组件化完成再考虑这个模块
此外, 由于成长手册中使用的转场工具类是Swift与OC混编, 而在组件内不支持swift类转oc头文件使用, 因此在该模块浪费了大量时间之后决定暂不进行组件化, 直接复制到两个项目中使用
image.png

第三阶段

  1. 通讯录及聊天模块处理时, 考虑到聊天的表情包处理相对较独立, 所以把它独立成一个小组件, 并且后面发现除了通讯录模块需要依赖这个小组件, 在消息首页的显示也需要, 刚好减少了后期重新再次拆分的工作量


    image.png
  2. 班级通知/学生作业/课程直播, 单个模块组件化基本按照前面的步骤进行, 把关联其他模块的逻辑释和/耦合严重的代码暂时注释掉

到这个阶段, 基本上大部分的模块已经组件化完成, 除了消息模块中涉及大量其他模块的消息类型, 还有成长记录涉及其他模块的入口管理和记录显示问题


第四阶段(在提测后继续进行)

  1. 在成长记录页面上, 顶部控件显示管理着其他各个模块入口 , 底部列表显示各种其他模块类型的记录数据 , 因此跟其他模块会有很大的耦合性


    image.png

    为了解决这个问题, 参考之前看过的路由实现模式, 最后决定采用注册组件的方式 , 由App进行管理注册显示的模块入口


    image.png
在App启动完成后, 第一时间注册应用入口, 并且配置相关信息, 包括组件的入口/标题/图标和应用Id, 同时注册一个初始化时特殊处理的回调, 方便一些组件的特殊配置; 注册完成后, 在成长记录组件内请求接口获取应用列表时就可以根据注册的组件map来匹配并展示模块入口以及跳转进组件内部
image.png
  1. 在处理底部展示的数据列表时, 由于之前已经考虑过多模块的情况, 因此成长记录的数据模型会通过一层中间层把各种模块的数据模型处理为统一的数据结构,再进行展示; 因此在组件化时, 把中间处理层也封装为注册管理对象, 在获取数据后通过回调, 交给外界来处理


    image.png
  2. 除了成长记录关联性高, 首页消息模块的关联性也高(班级通知/学生请假/直播通知/教学反馈/作业通知), 并且有更多的组件间跳转逻辑; 为了统一组件逻辑, 这里也同样用了跟成长记录一样的方式, 通过注册来管理各种消息类型


    image.png

    同样为了解耦和灵活性, 也有一个进入消息详情时的回调


    image.png
  3. 最后一步, 处理每一个组件内部需要跳转到其他组件的耦合逻辑; 这里有考虑过市场上已经成熟的一些路由第三方库, 但是大部分都比较复杂并且除了模块路由还有scheme路由/URI路由等等, 对于我们的项目来说, 只需要用到模块路由, 因此决定不使用这些库, 而是自己写一个路由管理组件之间的逻辑


    image.png
  4. 首先在每一个模块的头文件中定义好模块内部需要跳转其他模块的跳转唯一Id


    image.png
  5. 然后同样在app启动后, 注册该跳转Id对应的实际跳转的位置


    image.png
  6. 最后在模块内部通过跳转id 跳转的对应的页面并传递必要数据


    image.png
通过这种注册跳转Id的方式, 模块内部只需要告诉外界内部有这么一个跳转事件, 而不需要知道具体要跳转的页面是哪里, 完全交给外界来控制, 从而达到解耦

其他遇到的问题

1. 多架构问题

组件化时经常验证组件有效性时无法通过, 查看verbose相关信息后, 发现是编译链接i386架构时报错, i386是mac上32位模拟器的支持架构, 因此直接在.podSpec中配置指定验证的架构, 去掉i386 s.pod_target_xcconfig = { 'VALID_ARCHS' => 'x86_64 arm64 arm64e armv7' }

2.FFmpeg的取巧组件化

在封装FFmpeg作为底层组件时, 由于源代码包含.c .m , 而在组件内不支持引入C++系统模块, 因此一直会报错, 最终想出了一个比较绕弯路的方法: 首先新建一个静态库工程, 将FFmpeg中包含的.a文件, fdk-aac-ios插件, x264-iOS插件都拖入工程内


image.png

再将FFmpeg的工具处理源代码以及OC管理工具加入工程中


image.png

根据报错信息处理ffmpeg_filter.c中的相关代码后, 将该工程编译成一个x86_64架构的模拟器静态库, 以及一个arm64架构的真机静态库, 并用 lipo工具将它们合并成一个.a多架构静态库, 最终放到VSBaseFFmpeg组件中, 完成FFmpeg的组件化

3. 调试与代码同步

前面也说过, 之所以决定将已有App组件化, 是为了方便梅沙教育的升级以及后续两个App的开发, 但是cocoapods组件化时 , 如果通过私有索引库来链接到每一个组件的话, 每一个组件都是一个静态库, 对于静态库内部的代码是无法修改和调试的; 并且对于组件内部的每一次修改都需要重新提交上传打Tag, 再发布到索引库中, 然后才能更新, 不管是多人还是单人开发时都十分麻烦

  1. 为了解决这两个问题, 首先采用cocoaPods的本地引用方式来引用模块, 这样就可以直接调试组件内部代码
    image.png
  2. 查阅资料时发现git本身有子模块管理逻辑, 并且能做到仅引用子模块而不需全部下载, 某个子模块的提交有更新也可以及时提醒并更新该子模块, 基本上完全能解决现有问题, 所以最后决定不使用cocoapods的三方库管理模式, 采用git子模块管理

将两个以及组件文件夹放到同一个目录下, 以统一引用组件的的相对路径 , 然后通过git命令产生子模块关联git submodule add [子模块的git地址] [子模块名]
image.png
最终git父模块将会依赖于各个git子模块, .gitmodules 文件会有如下配置信息
image.png
在多人开发时, 直接拉取git父模块就可以拥有两个项目及关联的组件库
  1. 第一种拉取方式
    git clone xxx.git
    这样拉取下来的项目只有目录, 目录里面都是空的, 只有配置文件和依赖关系, 需要另外一部子模块操作
    git submodule init
    git submodule update

    image.png

  2. 第二种拉取方式
    git clone xx.git --recurse-submodules #递归拉取子模块

  3. 我们的项目中有一些文件比较大, 而gitlab服务端的设置有限制, 可能会导致一些组件拉取失败, 这时候可以用下面这个命令, 让每一个子模块执行checkout命令
    git submodule foreach git checkout master



最终项目组件结构如下:

image.png

项目组件化进行到这里已经基本上成功了, 剩下的是一些比较小的耦合逻辑, 会在之后的版本开发时慢慢解决

总结

  1. 完成组件化后, 对于之后的版本, 开发起来会变得更加巴适, 代码管理也会规范, 业务逻辑同样会更加清晰;
  2. 组件化过程最大的收获是对于耦合逻辑的解耦思考, 代码也变得更加健壮了;
  3. 这次项目组件化, 是对个人能力的一次巨大提升, 在以后编写代码时也会更加多的注意耦合及灵活性;
  4. 希望能找时间把教师端也组件化了(造福后人)。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • 一、背景 随着APP版本不断的迭代,新功能的不断增加,业务也会变的越来越复杂,APP业务模块的数量有可能还会继续增...
    please边去阅读 494评论 0 1
  • 1、为什么要项目组件化 随着 APP 版本不断的迭代,新功能的不断增加,业务也会变的越来越复杂,APP 业务模块 ...
    dashu52阅读 1,451评论 0 3
  • via --- EyreFree 零. 前言 “蜂鸟配送商家版”是一款针对商家打造的专业配送软件,有了这款应用,您...
    其实也没有阅读 1,316评论 0 7
  • 单项目结构: 将程序的所有功能以及依赖库都集中在一个项目下进行管理,不同业务或非业务通过包名区分,使得项目结构清晰...
    _Sisyphus阅读 856评论 0 2
  • 需求前提:希望App由多个业务组件组装起来,每个业务模块都可单独成为子App。解耦,快速开发! 流程 创建远程私有...
    Baffin阅读 969评论 0 6