我报名参加金石计划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
   └── ...

实操步骤

  1. 主库房新建 modules 目录,用以寄存组件/模块源码,目录 monorepo-demo 寄存壳工程源码
  2. 壳工程 Podfile 文件以本地依靠方法依靠各个模块
  3. 齐活了,便是那么简单

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-demoend

monorepo 的优势

  • 所有源码在同一个库房,分支办理与单体应用一样简单
  • MR 在同一个库房发出,队友阅读起来很顺畅
  • 保留 multirepo 的首要优势
    • 便于代码复用(单个组件/模块仍然能够独自发布版别,跨应用同享代码与 mutirepo 无异)
    • 模块独立开发、调试
    • 人员分工更清晰
    • 逼迫程序员下降代码耦合

monorepo 的缺乏

  • git 无法依据目录区分拜访权限,库房内全部代码开放给每个团队成员
  • 代码规划大到必定程度时 git 操作速度抵达瓶颈,影响 git 操作体会(这个中小规划团队不必考虑)。

提示:如果你要从 multirepo 方法迁移到 monorepo ,则只需把各模块库房 clone 到 modules 目录下,并切换到最新代码地点分支,然后删掉各模块目录下的 .git 文件,并按照「实操步骤」完结迁移即可。

组件化 + monorepo(单仓多模块) + multirepo(多仓多模块)

咱们各自的司情不同,再加上每种计划都有自己的限制和缺乏,为了能够满意咱们现实的需求,咱们继续探索。已然咱们清楚的预见了 monorepo 的缺陷,那么解决它其实也并不难。

首要咱们要知道 monorepo 与 multirepo 不是二选一的关系,而是主次的关系,已然是主次的关系,那么便是能够共存的。相信嘴角上扬的你现已悟了。

monorepo 优化计划

  • 稳定且独立的模块运用 multirepo 方法办理,像三方库一样
  • 十分驻事务(临时性的需求)能够运用 multirepo 方法办理,接下来某个版别就能够干掉,也不会污染 monorepo 库房(比如一个拉新的活动,只用几天,那么过了这几天,代码自然就没有存在的含义了)
  • 需求较高权限的模块能够运用 multirepo 方法办理,供给二进制库依靠即可

至此,咱们好像现已解决了所有已知痛点,或许接下来还会冒出新的痛点,不过请相信「方法总比困难多」。

小结

一点感悟共享给我们

  1. 没有最好的计划,只有当下最适合自己的计划
  2. 选计划不为追逐「潮流」,而是追逐「收益」
  3. 各计划的思维并非互相对立,彼此结合往往有奇效
  4. 计划选型要为后期调整留足空间
  5. 要深信「方法总比困难多」