2024年了你还不懂什么是monorepo?五分钟教会你!

现代前端工程开发的趋势和痛点

趋势


现代前端工程开发的已不再是之前的单一静态页面的开发,而是随着业务场景的多样性和复杂性在不断的演进,下面是一张来字节 web infra 团队在知乎分享的前端趋势概览图
在这里插入图片描述

  1. 第一个趋势是现代前端开发不再仅限于网页,而是涉及到的平台越来越多,比如 Web 端、Node、客户端和跨平台等。
  2. 第二个趋势是业务场景越来越多,复杂性也越来越大,特别是近年来也涌现了很多重前端交互的应用,比如 vscode、Figma。
  3. 第三个趋势就是随着业务场景的和多平台开发的出现,不可避免的使得前端团队规模在不断增大。

痛点


1、代码复用困难
在维护多个项目的时候,有一些逻辑很有可能会被多次用到,比如一些基础的组件、工具函数,在 polyrepo 中,需要为这些共享的代码单独维护一个仓库,之后会发布为单独的 npm 包供各个项目引用。
这样虽然能够解决代码复用的问题,但是之后这些公共依赖的升级会非常的繁琐,比如说现在所有的项目中都使用了 shared-ui 包的 1.1.0 版本,突然某个 ui 组件想要修改一些样式,就需要走下面的流程:

  • shared-ui 中修改样式
  • 发布一个 1.1.1版本的包
  • 所有的项目都需要将 shared-ui 更新到最新版本

如果升级完之后发现有问题,这些步骤还得重复的执行,依赖 shared-ui 的库越多,这个过程花费的时间就越多,同时这个过程也会存在一定的沟通成本。


2、重复的项目基建
Polyrepo 中,各个项目之间是割裂的状态,因此每个项目都需要频繁创建 git 仓库,配置 CILint 规则、构建等,而且为每个项目创建的基建后续都需要有人来维护(依赖升级)。


3、项目构建时间长
现代的前端项目开发已经离不开打包工具(webpackrollup),整体开发形式为项目开发时使用模块化机制开发, 经过构建工具打包形成成品代码, 成品代码最终在不支持模块化的浏览器中执行。虽然构建打包工具为前端开发提供了便利, 但也因为在项目运行之前需要提前将代码构建成一个成品整体,这导致在本地开发时也引入了新问题:
随着项目变得越来越大,全量构建整个项目就需要花费很长的时间,在本地开发时,无论每次修改多少代码都需要重新全量构建,大大降低了整体的开发效率。

一、Monorepo 简介及其与包管理工具(npm、yarn、pnpm)之间的关系

Monorepo模式:
Monorepo 是一种项目开发与管理的策略模式,它代表"单一代码仓库"(Monolithic Repository)。在 Monorepo 模式中,所有相关的项目和组件都被存储在一个统一的代码仓库中,而不是分散在多个独立的代码仓库中,这些项目之间还可能会有依赖关系。


包管理工具:
npmyarnpnpm 等是用来管理项目依赖、发布包、安装依赖的工具,它们都提供了对工作区(workspace)的支持,允许在单个代码库中管理多个项目或包。这种工作区支持在单个代码库中同时开发、测试和管理多个相关的项目,而无需使用多个独立的代码仓库。


关系:
这些包管理工具与 monorepo 的关系在于它们可以为 monorepo 提供依赖安装与依赖管理的支持,借助自身对 workspace 的支持,允许在 monorepo 中的不同子项目之间共享依赖项,并提供一种管理这些共享依赖项的方式,这可以简化依赖项管理和构建过程,并提高开发效率。

二、项目开发与管理模式

项目管理模式发展到现在,先后有三种项目管理模式的历史进程:
在这里插入图片描述
在这里插入图片描述

Monolithic(单体应用)开发模式

在软件开发的早期阶段,通常采用单体应用的开发模式。整个应用程序由一个单一的代码库、构建和部署流程组成。这种模式简单易懂,适合小型项目。

优点:

  • 易于集成和部署。所有的代码在一个仓库里面,不需要特别的集中管理和协调,也可以直接在本地部署调试。
  • 易于重用。所有的代码都在一个仓库中,开发人员开发的时候比较容易发现和重用已有的代码。
  • 易于规范代码。所有的代码在一个仓库当中就可以标准化依赖管理,规范化代码的风格。

缺点:

  • 代码维护性变差,随着功能以及代码量的大幅增加,代码功能耦合性增强。
  • 构建时间过长,任何小修改必须重新构建整个项目,这个过程往往很长。
  • 稳定性差,任意一个功能出现问题,可能导致整个应用挂掉。

Multirepo(多仓多模块)开发模式

为了解决单体应用的扩展性问题,团队开始将项目拆分为多个独立的仓库,每个仓库独立维护自己的代码和构建流程。这种模式更适合大型和复杂的项目,因为它提供了更好的隔离性和独立性,同时可以更灵活地管理多个团队的工作。

优点:

  • 每一个项目都有一个独立的仓库,职责单一。
  • 代码量和复杂性受控,项目由不同的团队独立维护、边界清晰。
  • 单个项目也易于自治开发测试部署和扩展,不需要集中管理集中协调。 利于进行权限控制,可以针对单个仓库来分配权限,权限分配粒度比较细。

缺点:

  • 代码和配置很难共享:每个仓库都需要做一些重复的工程化能力配置(如 eslint/test/ci 等)且无法统一维护,且不利于代码复用。
  • 依赖的治理复杂:模块越来越多,涉及多模块同时改动的场景增加。如何保障底层组件升级后,其引用到的组件也能同步更新到位。这点很难做到,如果没及时升级,各工程的依赖版本不一致,往往会引发一些意想不到的问题。
  • 开发人员缺乏对整个项目的整体认知:开发人员一般只关心自己的服务代码,看不到项目整体,造成缺乏对项目整体架构和业务目标整体性的理解。
  • 存储和构建消耗增加:假如多个工程依赖 pkg-a,那么每个工程下 node_modules 都会重复安装
    pkg-a,对本地磁盘内存和本地启动都是个很大的挑战。而且每个模块的发布都是相对独立的,当一次迭代修改较多模块时,总体发布时效就是每个发布流程的串联。对发布者来说是一个非常大的负担。

Monorepo (单仓多模块)开发模式

回归单体管理:Monorepo 是一种试图回归单体管理优势的方法,但保留了多仓库开发的某些优点。它允许在一个代码库中管理多个项目、组件或服务,提供更好的代码共享和重用性。

现代工具支持:现代的版本控制系统和工具链使得 Monorepo 开发模式更为可行,例如像 PnpmYarnLerna Turborepo 等工具,它们提供了更好的管理、构建和部署多个项目的能力。

优点:

  • 保留 multirepo 的主要优势
  • 代码复用
  • 模块独立管理
  • 分工明确,业务场景独立
  • 代码耦合度降低
  • 管理所有项目的版本控制更加容易和一致,降低了不同项目之间的版本冲突。
  • 可以统一项目的构建和部署流程,降低了配置和维护多个项目所需的工作量。

缺点:

  • Monorepo 可能随着时间推移变得庞大和复杂,导致构建时间增长和管理困难,git clonepull 的成本增加。
  • 权限管理问题:项目粒度的权限管理较为困难,容易产生非owner管理者的改动风险。

注意:Monorepo 绝不是简单地将代码搬到一个仓库(即不等于 Monolith)。

Monorepo 中,每个子模块仍然是独立的,有独立的版本,可以独立发包,不受其他模块的限制,最重要的是 Monorepobuildtest 都是增量的,只有发生更改的子模块会进行构建和测试,而不需要重新构建和测试整个代码库。这可以大大加快持续集成(CI)的速度,提高开发效率。
Mulitrepo 相比,Monorepo 中的子模块可以代码共享,可以最大程度复用依赖、复用工作流、复用基础配置。
单体仓库和多仓库两种方案能同时存在,一定是各有利弊的,不要将自己锁定到一种方案上,选择最合适的才是最好的。


3、前端包管理工具与 workspace 模式

Workspace 工作区

包管理工具通过 workspace 功能来支持 Monorepo 模式。Workspace 是指在一个代码库中管理多个相关项目或模块的能力。
包管理工具通过以下方式实现 workspace 的支持:

  1. 代码结构组织:在 Monorepo中,不同的项目或模块通常位于同一个代码库的不同目录中。包管理工具通过识别并管理这些目录结构,可以将它们作为独立的项目或模块进行操作。
  2. 共享依赖:Monorepo中的不同项目或模块可以共享相同的依赖项。包管理工具可以通过在根目录中维护一个共享的依赖项列表,以确保这些依赖项在所有项目或模块中都可用。
  3. 交叉引用:在 Monorepo中,不同项目或模块之间可能存在相互引用的情况。包管理工具需要处理这些交叉引用,以确保正确解析和构建项目之间的依赖关系。
  4. 版本管理:Monorepo中的不同项目或模块可能具有不同的版本。包管理工具需要能够管理和跟踪这些版本,并确保正确地安装和使用适当的版本。
  5. 构建和测试:包管理工具需要支持在 Monorepo中进行增量构建和测试。这意味着只有发生更改的项目或模块会重新构建和测试,而不需要重新构建和测试整个代码库。

前端目前最主流的三款包管理工具 npm7+yarnpnpm 都已经原生支持 workspace 模式,也就是说不管使用哪个包管理工具,我们都可以实现其与 monorepo 的配合,但最终依然选择 pmpm 作为包管理工具主要是由于 pnpm 很好的解决了 npmyarn 遗留的历史问题

npm 与 yarn 的历史遗留问题

  • 扁平化依赖算法复杂,需要消耗较多的性能,依赖串行安装还有提速空间。
  • 大量文件需要重复下载,对磁盘空间的利用率不足。(虽然在同一个项目中我不会重复的安装依赖 d了,但是如果我有100个项目,100个项目都需要用到某个包,那么这个包依然会被下载100次,也就是在磁盘的不同地方写入100次)
  • 扁平化依赖虽然解决了不少问题,但是随即带来了依赖非法访问的问题,项目代码在某些情况下可以在代码中使用没有被定义在 package.json中的包,这种情况就是我们常说的幽灵依赖

pnpm解决幽灵依赖

pnpm 是如何解决上述问题的?留着下期为大家揭晓吧!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

真的很上进

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值