我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击检查活动详情
布景
代码解耦和复用是一个十分重要的问题。早期咱们会以合理区分目录,提取公共组件的方法解决问题。
跟着项目增大,项目编译速度变慢(调试本钱变大),一起有了代码同享的需求,组件化开发是一个不错的选择,既能单个模块独立开发调试,又能多项目同享代码,看上去十分不错,研发功率提高也很明显(特别是调 UI 款式的时分)。
模块越来越多,涉及多模块一起改动的可能性急剧增加,终于在一次大规划重构中你吃到了组件化开发的第一个苦。怎样在壳工程一起修正多个模块并提交到对应库房?怎样发 MR 让队友 Review 你的代码?各模块版别怎样办理?等一系列问题让人头大。本文带你深刻复盘 iOS 组件化之路。
阅读本文你将了解:
- 什么是 monorepo
- 为什么要 monorepo
- iOS 怎样完结 monorepo
- monorepo 最佳实践
单体应用阶段
项目起步阶段,团队规划十分小,此刻适合「单体应用」。不过牢记,必定要合理区分目录,提取公共组件,尽可能做到不同功用/事务间解耦。为将来低本钱迁移到组件化计划留足空间。
此刻你的目录结构大概是这样的:
└── monorepo-demo
├── monorepo-demo
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ...
│ ├── SceneDelegate.swift
│ └── ViewController.swift
└── monorepo-demo.xcodeproj
├── project.pbxproj
...
优点:
- 代码办理本钱低
- 不需求额定的学习本钱
- 对
Merge Request
更友爱
缺陷:
- 代码质量简单失控
- 项目规划太大时,开发调试功率开端显著下降(构建时间比较长,调一个 UI 问题要等全量构建完才能看到作用)
- 跨项目复用代码遭到限制(不是不能跨项目复用代码)
- git 无法依据目录区分拜访权限,库房内全部代码开放给每个团队成员
组件化 + multirepo(多仓多模块)阶段
团队规划变大,人员分工开端清晰,此刻单体应用的缺陷益发杰出,此刻 multirepo 更适合咱们。multirepo 能够等效理解为把部分组件或模块作为三方库开发和运用,这样能够使人员分工更清晰,队员只需关心自己模块地点的库房,并在自己的模块内进行开发和调试,乃至能够独立提测单个模块。
此刻你的目录结构大概是这样的:
├── Podfile // 新增,用以装备依靠
├── Podfile.lock // 新增,用来锁定依靠版别,使团队内依靠的三方库版别共同,强烈建议归入git版别办理
├── Pods // 新增
│ ├── ModuleA // 咱们依靠的二方库
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── Source
│ │ ├── ModulA.swift
│ │ ...
│ ├── Headers
│ ├── Local Podspecs
│ ├── Manifest.lock
│ ├── Pods.xcodeproj
│ │ ├── project.pbxproj
│ │ ...
// 原有部分
├── monorepo-demo
│ ├── AppDelegate.swift
│ ...
│ ├── Info.plist
│ ├── SceneDelegate.swift
│ └── ViewController.swift
├── monorepo-demo.xcodeproj
│ ...
└── monorepo-demo.xcworkspace // 新增,后面咱们要在 Xcode 中翻开该文件进行开发而不再是 monorepo-demo.xcodeproj
└── ...
组件化开发的含义:
需求注意的点:
- 模块力度区分不太简单掌握
- 区分模块原则:基础组件、功用组件和事务模块
- 不要划得太细,组件数量激增会增大保护本钱
- 各模块分支办理(比较影响开发体会)
- 问题:事务迭代往往需求一起修正多个模块,此刻被修正的模块需求各自预备一个分支,而且需求一一对应
- 计划:被修正的模块版别与壳工程版别保持共同,拉出同名分支
- 仍然存在的问题:1. 为了模块版别统一,未改动的模块也要拉出同名分支,是剩余的操作
- 分支办理需求借助脚本完结
- 一起涉及多个模块的重构时需求在壳工程一起修正多个模块,并提交到各自库房。
- 计划:运用 git submodule 完结本地依靠,以 git flow 方法办理分支,供给配套脚本完结分支办理使命。
- 仍然存在的问题:1. 脚本执行功率比较低,且会偶现失利
- 一起涉及多个模块修正的 MR(Merge Request)需求在各自模块库房发出,显得比较涣散。
组件化 + monorepo(单仓多模块)阶段
monorepo(单仓多模块/项目),即把多个模块/项目源码放在同一个代码库房办理,乍一听这并不是一个好的主意,乃至有点扯,但这的确能解决不少问题,提高了开发体会,真的让功率提高了不少。
跟着组件/模块越来越多, multirepo 保护本钱越来越大,意外情况也变得更频频了(打包机打包失利更频频了),所以咱们意识到咱们的计划是时分改进了。
此刻你的目录结构大概是这样的:
.
├── modules // 新增目录
│ ├── ModuleA // A 模块源码,该模块具有独立开发调试能力
│ │ ├── Example
│ │ │ ├── ModuleA
│ │ │ │ ├── AppDelegate.swift
│ │ │ │ ...
│ │ │ │ └── ViewController.swift
│...
│ ├── ModuleB // B 模块源码,该模块具有独立开发调试能力
│ │ ├── Example
│ │ │ ├── ModuleB
│ │ │ │ ├── AppDelegate.swift
│ │ │ │ ...
│ │ │ │ └── ViewController.swift
│...
│...
└── monorepo-demo
├── Podfile
├── Podfile.lock
├── Pods
│ ├── Alamofire
│...
│ └── Target Support Files
│ ├── Alamofire
│ │ ├── ...
│ ├── ModuleA
│ │ ├── ...
│ ├── ModuleB
│ │ ├── ...
│...
├── monorepo-demo
│ ├── AppDelegate.swift
│ ├── ...
│ ├── SceneDelegate.swift
│ └── ViewController.swift
├── monorepo-demo.xcodeproj
│ ...
└── monorepo-demo.xcworkspace
└── ...
实操步骤:
- 主库房新建 modules 目录,用以寄存组件/模块源码,目录 monorepo-demo 寄存壳工程源码
- 壳工程 Podfile 文件以本地依靠方法依靠各个模块
- 齐活了,便是那么简单
Podfile 示例:
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target'monorepo-demo'do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
pod'Alamofire'
# 以本地依靠方法依靠各二方库
pod'ModuleA',:path=>'../modules/moduleA'
pod'ModuleB',:path=>'../modules/moduleB'
pod'ModuleC',:path=>'../modules/moduleC'
# Pods for monorepo-demo
end
monorepo 的优势:
- 所有源码在同一个库房,分支办理与单体应用一样简单
- MR 在同一个库房发出,队友阅读起来很顺畅
- 保留 multirepo 的首要优势
- 便于代码复用(单个组件/模块仍然能够独自发布版别,跨应用同享代码与 mutirepo 无异)
- 模块独立开发、调试
- 人员分工更清晰
- 逼迫程序员下降代码耦合
monorepo 的缺乏:
- git 无法依据目录区分拜访权限,库房内全部代码开放给每个团队成员
- 代码规划大到必定程度时 git 操作速度抵达瓶颈,影响 git 操作体会(这个中小规划团队不必考虑)。
提示:如果你要从 multirepo 方法迁移到 monorepo ,则只需把各模块库房 clone 到 modules 目录下,并切换到最新代码地点分支,然后删掉各模块目录下的
.git
文件,并按照「实操步骤」完结迁移即可。
组件化 + monorepo(单仓多模块) + multirepo(多仓多模块)
咱们各自的司情不同,再加上每种计划都有自己的限制和缺乏,为了能够满意咱们现实的需求,咱们继续探索。已然咱们清楚的预见了 monorepo 的缺陷,那么解决它其实也并不难。
首要咱们要知道 monorepo 与 multirepo 不是二选一的关系,而是主次的关系,已然是主次的关系,那么便是能够共存的。相信嘴角上扬的你现已悟了。
monorepo 优化计划:
- 稳定且独立的模块运用 multirepo 方法办理,像三方库一样
- 十分驻事务(临时性的需求)能够运用 multirepo 方法办理,接下来某个版别就能够干掉,也不会污染 monorepo 库房(比如一个拉新的活动,只用几天,那么过了这几天,代码自然就没有存在的含义了)
- 需求较高权限的模块能够运用 multirepo 方法办理,供给二进制库依靠即可
至此,咱们好像现已解决了所有已知痛点,或许接下来还会冒出新的痛点,不过请相信「方法总比困难多」。
小结
一点感悟共享给我们:
- 没有最好的计划,只有当下最适合自己的计划
- 选计划不为追逐「潮流」,而是追逐「收益」
- 各计划的思维并非互相对立,彼此结合往往有奇效
- 计划选型要为后期调整留足空间
- 要深信「方法总比困难多」