Monorepo、Lerna、Turborepo

77 阅读14分钟

面试题

面试1、单体仓库monorepo解决了哪些问题?单体仓库有哪些工具及它们的对比优劣势

单体仓库(Monorepo) 是一种将多个项目或包存储在同一个代码仓库中的开发模式。它解决了传统多仓库(Multi-Repo)模式中的许多问题,特别是在代码共享、依赖管理、版本控制和开发效率方面。

1. 代码共享与复用

(1) 问题

  • 在多仓库模式下,共享代码需要通过发布包或复制代码的方式,导致维护困难。
  • 不同仓库之间的代码复用成本高,容易产生重复代码。

(2) 解决

  • Monorepo 中所有项目共享同一个代码库,可以轻松引用和复用代码。
  • 通过模块化设计,将公共代码提取为共享库,减少重复代码。

2. 依赖管理

(1) 问题

  • 在多仓库模式下,不同项目可能使用不同版本的依赖,导致冲突和重复安装。
  • 依赖版本不一致可能导致构建失败或运行时错误。

(2) 解决

  • Monorepo 中所有项目使用统一的依赖管理工具(如 npmyarnpnpm),确保依赖版本一致。
  • 通过工具(如 Lerna、Nx)实现依赖的提升和共享,减少重复安装。

3. 版本控制

(1) 问题

  • 在多仓库模式下,跨仓库的版本控制和发布流程复杂。
  • 需要手动同步多个仓库的版本号,容易出错。

(2) 解决

  • Monorepo 中可以通过工具(如 Lerna、Changesets)统一管理版本和发布流程。
  • 在一个提交中完成跨项目的版本更新,确保一致性。

4. 开发效率

(1) 问题

  • 在多仓库模式下,切换项目和配置开发环境耗时。
  • 每个仓库需要单独配置构建工具、测试工具等,增加了开发成本。

(2) 解决

  • Monorepo 中所有项目共享相同的开发环境,提升开发效率。
  • 通过工具(如 Nx、Turborepo)实现增量构建和测试,减少构建时间。

5. 跨项目重构

(1) 问题

  • 在多仓库模式下,跨项目重构需要同步多个仓库,容易出错。
  • 重构后的代码需要分别提交和发布,增加了复杂性。

(2) 解决

  • Monorepo 中可以在一个提交中完成跨项目重构,确保一致性。
  • 通过工具(如 Nx、Bazel)实现跨项目的依赖分析和重构支持。

6. 代码一致性

(1) 问题

  • 在多仓库模式下,不同仓库可能使用不同的代码风格、工具和配置,导致代码不一致。
  • 维护统一的代码风格和配置成本高。

(2) 解决

  • Monorepo 中可以通过统一的配置文件和工具(如 ESLint、Prettier)确保代码一致性。
  • 所有项目共享相同的代码风格和配置,减少维护成本。

7. CI/CD 集成

(1) 问题

  • 在多仓库模式下,每个仓库需要单独配置 CI/CD 流水线,增加了维护成本。
  • 跨仓库的构建和测试流程复杂,容易出错。

(2) 解决

  • Monorepo 中可以通过统一的 CI/CD 配置管理所有项目的构建和测试流程。
  • 通过工具(如 Nx、Turborepo)实现增量构建和测试,提升 CI/CD 效率。

8. 团队协作

(1) 问题

  • 在多仓库模式下,团队成员需要频繁切换仓库,增加了沟通和协作成本。
  • 不同仓库的权限管理和代码审查流程复杂。

(2) 解决

  • Monorepo 中所有项目共享同一个代码库,简化了团队协作流程。
  • 通过统一的权限管理和代码审查工具,提升团队协作效率。

9. 总结
问题Monorepo 的解决方案
代码共享与复用所有项目共享同一个代码库,轻松引用和复用代码。
依赖管理统一依赖管理工具,确保依赖版本一致。
版本控制统一管理版本和发布流程,确保一致性。
开发效率共享开发环境,提升开发效率。
跨项目重构在一个提交中完成跨项目重构,确保一致性。
代码一致性统一配置文件和工具,确保代码一致性。
CI/CD 集成统一 CI/CD 配置,提升构建和测试效率。
团队协作简化协作流程,提升团队协作效率。

通过采用 Monorepo,可以有效解决多仓库模式下的许多问题,提升代码共享、依赖管理、版本控制和开发效率。

1. 主流 Monorepo 工具
工具语言支持包管理器集成依赖管理构建工具集成学习曲线社区生态
Nx多语言(JS/TS等)支持 npm/yarn/pnpm高效依赖管理支持中等活跃
LernaJavaScript/TypeScript支持 npm/yarn依赖提升需要配置成熟
TurborepoJavaScript/TypeScript支持 npm/yarn/pnpm增量构建支持新兴
RushJavaScript/TypeScript支持 npm/yarn/pnpm严格依赖管理支持微软支持
Bazel多语言(JS/Java等)高效增量构建内置强大
Pnpm WorkspacesJavaScript/TypeScript仅 pnpm高效依赖管理需要配置新兴

2. 工具详细介绍及对比

(1) Nx

  • 特点:支持多语言(JavaScript、TypeScript、Angular、React、Node.js 等)。内置代码生成器和依赖图可视化工具。支持增量构建和测试,性能优秀。与主流框架(如 Angular、React)深度集成。
  • 优势:功能全面,适合大型项目。增量构建和缓存机制显著提升构建速度。社区活跃,文档丰富。
  • 劣势:配置复杂,学习曲线较高。对小型项目可能显得过于重量级。

(2) Lerna

  • 特点:专注于 JavaScript/TypeScript 项目。支持依赖提升(hoisting),减少重复依赖。与 npm/yarn 集成良好。
  • 优势:简单易用,适合中小型项目。社区成熟,生态丰富。
  • 劣势:缺乏增量构建支持,性能较差。依赖管理不够严格,可能导致幽灵依赖问题。

(3) Turborepo

  • 特点:专注于 JavaScript/TypeScript 项目。支持增量构建和缓存,性能优秀。与 npm/yarn/pnpm 集成良好。
  • 优势:轻量级,配置简单。增量构建和缓存机制显著提升构建速度。
  • 劣势:功能相对较少,适合中小型项目。社区和生态仍在发展中。

(4) Rush

  • 特点:由微软开发,适合大型企业级项目。支持严格的依赖管理和版本控制。内置增量构建和链接机制。
  • 优势:严格依赖管理,避免幽灵依赖。适合超大型 Monorepo。
  • 劣势:配置复杂,学习曲线高。社区相对较小。

(5) Bazel

  • 特点:由 Google 开发,支持多语言(JavaScript、Java、C++ 等)。高效的增量构建和缓存机制。适合超大型项目。
  • 优势:构建性能极佳,适合跨语言项目。强大的扩展性和灵活性。
  • 劣势:配置复杂,学习曲线陡峭。对 JavaScript/TypeScript 生态支持较弱。

(6) Pnpm Workspaces

  • 特点:基于 pnpm 的 Monorepo 支持。使用硬链接和符号链接管理依赖,节省磁盘空间。配置简单,适合中小型项目。
  • 优势:依赖管理高效,节省磁盘空间。与 pnpm 无缝集成。
  • 劣势:功能较少,缺乏增量构建支持。仅支持 pnpm,生态相对较小。

3. 工具选择建议
场景推荐工具理由
中小型 JavaScript 项目Lerna / Turborepo简单易用,配置少,适合快速上手。
大型 JavaScript 项目Nx / Rush功能全面,支持增量构建和严格依赖管理,适合复杂项目。
跨语言项目Bazel支持多语言,构建性能优秀,适合超大型项目。
磁盘空间敏感项目Pnpm Workspaces依赖管理高效,节省磁盘空间。
企业级项目Rush / Nx严格依赖管理,适合团队协作和大型项目。

4. 总结
Monorepo 解决的问题
  • 代码共享、依赖管理、版本控制、开发效率和跨项目重构。
工具对比
  • Nx:功能全面,适合大型项目。
  • Lerna:简单易用,适合中小型项目。
  • Turborepo:轻量级,性能优秀,适合中小型项目。
  • Rush:严格依赖管理,适合企业级项目。
  • Bazel:跨语言支持,构建性能极佳。
  • Pnpm Workspaces:依赖管理高效,节省磁盘空间。

根据项目规模、技术栈和团队需求选择合适的工具,可以显著提升 Monorepo 的开发效率和维护性。

面试二、单体仓库依赖的管理方式以及包的发布等是怎么处理的?

单体仓库(Monorepo) 中,依赖管理和包的发布是核心问题之一。由于多个项目或包共享同一个代码库,如何高效地管理依赖、避免冲突,以及如何发布包,是 Monorepo 需要解决的关键问题。以下是 Monorepo 中依赖管理和包发布的常见方式:


依赖管理

(1) 依赖提升(Hoisting)
  • 问题:在 Monorepo 中,多个包可能依赖同一个第三方库,如果每个包都独立安装依赖,会导致重复安装和版本冲突。

  • 解决方案:使用工具(如 Lerna、Nx、pnpm)将共同的依赖提升到 Monorepo 的根目录。

    通过 node_modules 的符号链接或硬链接机制,减少重复安装。

    使用 Lerna 的 hoist 功能:

    css
     体验AI代码助手
     代码解读
    复制代码
    lerna bootstrap --hoist
    

    使用 pnpm 的 Workspace 功能:

     体验AI代码助手
     代码解读
    复制代码
    pnpm install
    
(2) 依赖共享
  • 问题:Monorepo 中的多个包可能需要共享某些内部工具或库。

  • 解决方案:将共享代码提取为独立的包,并在 Monorepo 中引用。

    使用工具(如 Nx、Turborepo)管理包之间的依赖关系。

  • 示例:在 packages/shared 中定义共享代码,其他包通过 package.json 引用:

    json
     体验AI代码助手
     代码解读
    复制代码
    {
      "dependencies": {
        "shared": "workspace:*"
      }
    }
    
(3) 依赖版本一致性
  • 问题:不同包可能依赖不同版本的第三方库,导致冲突。

  • 解决方案

    使用工具(如 Lerna、Rush)统一管理依赖版本。

    在 Monorepo 的根目录中定义统一的依赖版本。

  • 示例:使用 Lerna 的 fixed 模式:

    json
     体验AI代码助手
     代码解读
    复制代码
    {
      "version": "fixed"
    }
    

包的发布

(1) 版本管理
  • 问题:在 Monorepo 中,多个包可能需要独立发布,但版本号需要保持一致或按需更新。

  • 解决方案:使用工具(如 Lerna、Changesets)管理包的版本号。支持独立版本(Independent Mode)或统一版本(Fixed Mode)。

  • 示例

    使用 Lerna 的独立版本模式:

    css
     体验AI代码助手
     代码解读
    复制代码
    lerna version --conventional-commits
    

    使用 Changesets 管理版本:

    csharp
     体验AI代码助手
     代码解读
    复制代码
    changeset add
    changeset version
    
(2) 发布流程
  • 问题:在 Monorepo 中,发布多个包需要确保依赖关系和版本号的一致性。

  • 解决方案

    • 使用工具(如 Lerna、Nx、Rush)自动化发布流程。
    • 支持增量发布(只发布有变化的包)。
  • 示例:使用 Lerna 发布包:

     体验AI代码助手
     代码解读
    复制代码
    lerna publish
    

    使用 Nx 发布包:

    ini
     体验AI代码助手
     代码解读
    复制代码
    nx run-many --target=publish
    
(3) 私有包管理
  • 问题:Monorepo 中的某些包可能是私有的,不需要发布到公共注册表。

  • 解决方案:在 package.json 中设置 "private": true,避免发布私有包。

    使用私有注册表(如 Verdaccio)管理私有包。

  • 示例:在 package.json 中标记私有包:

    json
     体验AI代码助手
     代码解读
    复制代码
    {
      "private": true
    }
    

小结

依赖管理
  • 依赖提升:通过工具(如 Lerna、pnpm)减少重复安装。
  • 依赖共享:将共享代码提取为独立包。
  • 版本一致性:统一管理依赖版本。
包发布
  • 版本管理:使用工具(如 Lerna、Changesets)管理版本号。

  • 发布流程:自动化发布流程,支持增量发布。

  • 私有包管理:标记私有包,使用私有注册表。

面试三、pnpm跟npm有什么区别?包管理工具corepack了解吗?

pnpm和npm我也是了解的,但是corepack这个包管理工具确实不太熟悉

pnpmnpm 都是 Node.js 的包管理工具

pnpm 和 npm 的区别

(1) 磁盘空间利用率
  • pnpm

    使用 硬链接(hard link)符号链接(symlink) 的方式存储依赖包。

    所有项目的依赖包都会链接到一个全局的存储目录(~/.pnpm-store),避免了重复下载和存储。

    显著节省磁盘空间,尤其是在多个项目使用相同依赖时。

  • npm

    每个项目都会在 node_modules 中完整地下载和存储依赖包。

    如果多个项目使用相同的依赖,每个项目都会有一份独立的副本,导致磁盘空间浪费。


(2) 安装速度
  • pnpm

    由于依赖包是从全局存储中链接到项目,安装速度通常比 npm 更快。

    尤其是在依赖包已经存在于全局存储时,安装几乎是瞬间完成的。

  • npm

    每次安装依赖时都需要下载并解压包,速度相对较慢。


(3) node_modules 结构
  • pnpm

    使用 扁平化 + 符号链接 的结构。

    每个依赖包只会存在于一个地方(全局存储),项目中的 node_modules 只包含符号链接。

    避免了依赖重复和幽灵依赖(phantom dependencies)问题。

  • npm

    使用 扁平化结构,所有依赖包会被提升到 node_modules 的根目录。

    可能导致依赖冲突和幽灵依赖问题(即未在 package.json 中声明的依赖被错误地使用)。


(4) 严格性
  • pnpm

    更加严格,确保只有 package.json 中声明的依赖可以被访问。

    避免了幽灵依赖问题,提高了项目的可维护性和稳定性。

  • npm

    由于扁平化结构,未在 package.json 中声明的依赖可能被错误地访问,导致潜在的问题。


(5) 兼容性
  • pnpm

    完全兼容 package.jsonnpm 的生态系统。

    支持 npm 的大部分命令(如 installrunpublish 等)。

    可以无缝替换 npm

  • npm

    是 Node.js 的默认包管理工具,兼容性最好。


(6) Monorepo 支持
  • pnpm

    内置对 Monorepo 的支持,通过 pnpm-workspace.yaml 配置文件管理多个子项目。

    依赖共享和链接机制非常适合 Monorepo 场景。

  • npm

    需要借助第三方工具(如 lerna)来实现 Monorepo 支持。


(7) 生态和社区
  • pnpm

    社区规模较小,但增长迅速。

    在大型项目和 Monorepo 中越来越受欢迎。

  • npm

    是 Node.js 的官方包管理工具,社区和生态系统非常成熟。

总结

pnpm 和 npm 的对比
特性pnpmnpm
磁盘空间利用率高(通过硬链接节省空间)低(每个项目独立存储依赖)
安装速度快(依赖全局存储)较慢(每次都需要下载)
node_modules 结构扁平化 + 符号链接扁平化
严格性严格(避免幽灵依赖)较宽松(可能存在幽灵依赖)
Monorepo 支持内置支持需要第三方工具(如 lerna)
生态和社区较小但增长迅速非常成熟

Corepack 是什么?

(1) 核心功能

  • Corepack 是 Node.js 官方提供的一个包管理器管理工具,用于管理不同的 JavaScript 包管理器(如 npmyarnpnpm 等)。
  • 它的目标是简化包管理器的安装和使用,确保开发者可以在不同项目中使用一致的包管理器版本。

(2) 主要用途

  • 统一管理包管理器:Corepack 允许你轻松地在项目中切换和使用不同的包管理器,而无需手动安装或配置。
  • 确保版本一致性:通过 Corepack,你可以为项目指定特定的包管理器版本,避免因版本不一致导致的问题。

(3) 使用示例

  • 启用 Corepack

    bash
     体验AI代码助手
     代码解读
    复制代码
    corepack enable
    
  • 激活特定包管理器

    sql
     体验AI代码助手
     代码解读
    复制代码
    corepack prepare pnpm@latest --activate
    
  • 在项目中使用指定版本的包管理器: 在 package.json 中指定:

    perl
     体验AI代码助手
     代码解读
    复制代码
    {
      "packageManager": "pnpm@7.0.0"
    }
    

Corepack 的作用

  • Corepack 是一个包管理器管理工具,用于简化包管理器的安装和使用。

  • 它可以帮助开发者在不同项目中使用一致的包管理器版本,避免版本冲突。

参考:

现代前端工程为什么越来越离不开 Monorepo?

Monorepo-多包单仓库的开发模式

lerna+yarn workspace+monorepo项目的最佳实践

团队工程实践 - 打造monorepo工作流

精读《Monorepo 的优势》

深入 lerna 发包机制 —— lerna publish

现代前端工程化-基于 Monorepo 的 lerna 详解(从原理到实战)

开源项目都在用 monorepo,但是你知道居然有那么多坑么?

Monorepo 的这些坑,我们帮你踩过了!