1. 弁言
本周精读的文章是 The many Benefits of Using a Monorepo。
如今引见 Monorepo 的文章许多,能够分为以下几类:直接引见 Lerna API 的;引见怎样从自力堆栈迁移到 Lerna 的;经由过程举例子申明 Monorepo 重要性的。
本文属于第三种,从 Android 与 IOS 的开辟故事申清晰明了 Monorepo 的重要性。
笔者之所以挑选这篇文章,不是因为其故事写的好,而是承认这类具有普适性的处理思绪。毕竟 Lerna 作为 Monorepo 的完成之一也并不精美绝伦,而差别场景对 Monorepo 依靠的缘由、功用也有所差别,所以希望借这篇文章,从理论上诠释清晰为何会发作 Monorepo,以及 Monorepo 能够处理哪些题目,如许在事情碰到题目时,才想清晰本身要的是什么。
2. 概述
作者的一个项目是 PDF 效劳,简称 PSPDFKit,须要同时统筹 Android 与 IOS 平台,项目标生长阅历了以下几个阶段。
初始阶段
在 2011 到 2013 年间,PSPDFKit 仅支撑 IOS 平台,但终究项目须要支撑 Android,因而开了一个新堆栈安排 Android 代码。Android 堆栈的代码不仅在 UI 上差别,同时剖析 PDF 文档的中心代码也差别,这是因为 IOS 平台上运用内置 PDF 衬着引擎同时做了一些营业拓展,但运用的 OC 代码没法在 Android 运用。
终究新建了两个堆栈 PSPDFKit-Android
与 Core
。
堆栈 Core 中代码依靠 Android 平台 JNI 的支撑,所以并不能完成 Core 一处修正,两处都见效的希望,而我们又希望双方功用一向兼容,且削减分支过量带来的潜伏的争执,因而花了良久才意想到应当将这两个堆栈兼并起来。
斟酌运用 Monorepo
因为 Android 的整套流程本身掌握的,因而老是能够疾速修复用户提出的 BUG,但是 IOS 供应的 CGPDF 总会赶上种种题目。所以在 2014 年,我们开启了一个巨大的项目,重写 IOS 的 Core 库。有三中体式格局可供挑选:
- 在 IOS 代码中援用
PSPDFKit-Android
。 - 将
PSPDFKit-Android
提取到Core
堆栈中并离别保护。 - 将 IOS 与 Android 代码兼并到一个堆栈中。
经由议论,终究作者的团队挑选了第三种计划,因而目次构造相似以下:
- ios-platform
- android-platform
- core
惯例
Web 与背景效劳代码一向是一个惯例,我们以为这些内容相对自力,所以没有将其代码安排到 Monorepo 中。
直到一年后,最先探究 WebAssembly 时,PSPDFKit-web 模块就涌现了,因为能够应用 WebAssembly 将 Core 的代码编译并在 Web 平台运用,因而 Core 堆栈与 Web 堆栈的关联变得非常严密,终究,我们将 Web、Server 也都迁移到 Monorepo 中了。
题目
Monorepo 白璧微瑕,但作者照样枚举了一些瑕玷。
因为源码在一同,堆栈变动非常罕见,存储空间也变得很大,以至几 GB,CI 测试运转时候也会变长。即便如此,团队中任何人都不想回到 git submodules 多堆栈的体式格局。
3. 精读
总的来讲,虽然拆份子堆栈、拆份子 NPM 包(For web)是举行项目断绝的自然计划,但当堆栈内容涌现关联时,没有任何一种调试体式格局比源码放在一同更高效。
工程化的终究目标是让营业开辟能够 100% 聚焦在营业逻辑上,那末这不仅仅是脚手架、框架须要从自动化、设想上处理的题目,这涉及到堆栈治理的设想。
一个抱负的开辟环境能够笼统成如许:
“只体贴营业代码,能够直接跨营业复用而不体贴复用体式格局,调试时一切代码都在源码中。”
在前端开辟环境中,多 Git Repo,多 Npm 则是这个抱负的阻力,它们致使复用要体贴版本号,调试须要 Npm Link。
别的关于多堆栈的瑕玷,文中另有一些没有提到的要素,这里一并枚举出来:
治理、调试难题
多个 git 堆栈治理起来自然是贫苦的。关于功用相似的模块,如果拆成了多个堆栈,不管关于多人合作照样自力开辟,都须要翻开多个堆栈页面。
虽然 vscode 经由过程 Workspaces 处理多堆栈治理的题目,但在多人合作的场景下,没法保证每一个人的环境设置一致。
关于共用的包经由过程 Npm 装置,如果不能接收调试编译后的代码,或每次 npm link 一下,就没有办法调试依靠的子包。
分支治理混乱
如果一个堆栈供应给 A、B 两个项目用,而 B 项目优先开辟了功用 b,没法与 A 项目兼容,此时就要在这个堆栈开一个 feature/b
的分支支撑这个功用,并且在将来兼并到骨干同步到项目 A。
一旦须要开分支的组件变多了,且之间出来依靠关联,分支治理庞杂度就会呈指数上升。
依靠关联庞杂
自力堆栈间组件版本号的保护须要手动操纵,因为源代码不在一同,所以没有办法团体剖析依靠,自动化治理版本号的依靠。
三方依靠版本能够不一致
一个自力的包具有一套自力的开辟环境,难以保证子模块的版本和主项目完全一向,就存在运转结果不一致的风险。
占用总空间大
一般情况下,一个公司的营业项目只要一个骨干,多 git repo 的体式格局浪费了大批存储空间反复装置比方 React 等大型模块,时候久了能够会占用几十 GB 的分外空间,关于没有外接硬盘的同砚来讲,按期清算不必的项面前目今 node_modules
也是一件贫苦事。
不利于团队合作
一个大项目能够会用到数百个二方包,差别二方包的保护频次差别,权限差别,堆栈位置也差别,主堆栈对它们的依靠体式格局也差别。
一旦个中一个包举行了非一般修改,就会影响到全部项目,而我们精力有限,只盯着主堆栈,往往会栽在不起眼的二方包宣布上。
所以关于一个非常庞杂,又具有手艺应战的大型体系在合作职员多的情况下涌现题目标几率非常大,须要经由过程 Review 轨制防止毛病的发作,那末将一切相干的源码聚合在一个堆栈下,是更好治理的。
抱负 monorepo 的设想
参考 Lerna 的范例,以 packages
作为子模块根文件夹,笔者设想一个抱负的 monorepo 构造:
.
├── packages
│ ├─ module-a
│ │ ├─ src # 模块 a 的源码
│ │ └─ package.json # 自动天生的,仅模块 a 的依靠
│ └─ module-b
│ ├─ src # 模块 b 的源码
│ └─ package.json # 自动天生的,仅模块 b 的依靠
├── tsconfig.json # 设置文件,对全部项目见效
├── .eslintrc # 设置文件,对全部项目见效
├── node_modules # 全部项目只要一个外层 node_modules
└── package.json # 包括全部项目一切依靠
一切全局设置文件只要一个,如许不会致使 IDE 碰到子文件夹中的设置文件,致使全局设置失效或非常。node_modules
也只要一个,既保证了项目依靠的一致性,又防止了依靠被反复装置,节约空间的同时还提高了装置速率。
兄弟模块之间经由过程模块 package.json
定义的 name
互相援用,保证模块之间的自力性,但又不须要真正宣布或装置这个模块,经由过程 tsconfig.json
的 paths
与 webpack
的 alias
配合完成假造模块途径的结果。
再连系 Lerna 依据联动宣布功用,使每一个子模块都能够自力宣布。
4. 总结
Lerna 是业界知名度最高的 Monorepo 治理东西,功用完全。但因为通用性请求非常高,须要支撑恣意项目间 Monorepo 的组合,因而在 packages
文件夹下的设置文件照样与自力堆栈坚持一致,如许在 TS 环境下会形成设置截断的题目。同时包之间的援用也经由过程更通用的 symlink 完成,这致使了照样要在子模块目次存在 node_modules
文件夹,而且结果依靠项目初始化敕令。
如果加一些限制前提,比方基于 Webpack + Typescript 环境的 Monorepo,能够换一套思绪,应用这些东西本身运转时功用,削减更多模版代码或设置文件,进一步提拔 Monorepo 的结果。
关于别号映照,对 symlink 与 alias 举行对照:
- symlink: 更通用,合适任何构建器。但须要初始化,且在每一个关联模块下新增
node_modules
文件夹。 - alias: 限制构建器。但不须要初始化,不新增文件夹,以至能够运转时动态修正别号设置。
可见如果限制了构建器,别号映照能够做得更轻量,且无需初始化。
本日的题目是,你的项目须要运用 Monorepo 吗?你对 Monorepo 有其他请求吗?
如果你想介入议论,请 点击这里,每周都有新的主题,周末或周一宣布。前端精读 – 帮你挑选靠谱的内容。
关注
前端精读微信民众号
<img width=200 src=”https://img.alicdn.com/tfs/TB…;>
special Sponsors
版权声明:自在转载-非商用-非衍生-坚持签名(
创意同享 3.0 许可证)