前端工程化实践:Monorepo与Lerna管理

前端工程化实践中,Monorepo(单仓库)管理和Lerna是两种流行的方式,用于大型项目或组件库的组织和版本管理。

Monorepo简介

Monorepo(单仓库)是指在一个Git仓库中管理多个相关项目的开发方式。这种方式的优点在于:

集中式管理:所有项目都在一个仓库中,方便代码共享、版本同步和协同开发。 模块化:可以创建独立的模块,方便复用和维护。 统一的CI/CD:一次配置,全仓库生效,简化持续集成和部署流程。 更好的依赖管理:可以更容易地管理项目间的依赖关系。

Lerna简介

Lerna是一个命令行工具,用于在Monorepo中管理多包项目。它提供了版本管理和发布功能,使得在单个仓库中管理多个npm包变得简单。

Lerna的核心概念有:

  • Packages:Monorepo中的独立npm包。
  • Versions:每个包可以有自己的版本,可以是固定的或共享的。
  • Bootstrapping:初始化所有包的依赖关系,确保每个包都能正常工作。
  • Publishing:发布包到npm,可以是逐个包发布,也可以是批量发布。

安装Lerna

首先,确保安装Node.js和npm。然后,在项目根目录安装Lerna:

npm install --save-dev lerna
# 或
yarn add --dev lerna

初始化Lerna项目

在项目根目录运行以下命令初始化Lerna:

npx lerna init

这将创建一个lerna.json配置文件和一个packages目录,用于存放各个包。

创建和管理包

在packages目录下创建新包:

mkdir packages/my-package
cd packages/my-package
npm init -y
# 或
yarn init -y

在包内编写代码,例如index.js和package.json。

使用Lerna命令

Bootstrap:初始化所有包的依赖关系。

  npx lerna bootstrap

Add:在包之间添加依赖。

  npx lerna add @scope/my-package

Publish:发布包到npm。

  npx lerna publish

List:查看项目中的包。

  npx lerna ls

Lerna配置

lerna.json中,可以配置Lerna的行为,例如:

{
  "version": "independent", // 或 "fixed"
  "packages": ["packages/*"],
  "command": {
    "publish": {
      "ignore": ["**/node_modules/**"]
    }
  }
}
  • version:指定版本策略,可以是fixed(所有包共享同一版本号)或independent(每个包有自己的版本号)。
  • packages:指定包含包的目录路径。
  • command.publish.ignore:在发布时忽略的文件或目录。

性能优化

  • 独立版本:使用independent版本策略,可以单独发布每个包,避免不必要的发布。
  • Selective Publishing:使用--since或--scope参数,只发布更改的包。
  • Workspaces:配合Yarn Workspaces使用,减少安装时间和磁盘空间占用。

Lerna的高级特性

异步操作和锁定

Lerna支持异步操作,这在处理大型项目时非常有用。在lerna.json中设置concurrency属性可以限制并发执行的任务数量,防止资源过度消耗。

{
  "concurrency": 4
}

Lerna还使用npm的package-lock.json或yarn.lock文件来确保每次安装时的依赖一致性。当lerna bootstrap执行时,它会按照文件中的锁定版本安装依赖,保证所有开发者在本地构建时使用相同的依赖版本。

自定义脚本

Lerna允许在lerna.json中定义自定义脚本,以便在项目中执行特定任务。例如,创建一个预发布脚本:

{
  "scripts": {
    "prepublishOnly": "tsc"
  }
}

这将在发布包之前运行TypeScript编译器。

工作流集成

Lerna可以与常见的CI/CD工具(如Jenkins、CircleCI、GitHub Actions等)集成,实现自动化测试、构建和发布。在.gitlab-ci.yml、.travis.yml或其他CI配置文件中,配置相应的Lerna命令。

例如,在GitHub Actions中:

name: CI

on:
push:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: npm ci
- name: Bootstrap Lerna
run: npm run bootstrap
- name: Run Tests
run: npm test
- name: Publish
if: startsWith(github.ref, ‘refs/tags’)
run: npx lerna publish from-git --yes

Monorepo的最佳实践

  • 清晰的模块划分:确保每个包都有明确的边界和用途,避免过度耦合。
  • 版本策略:根据项目需求选择固定或独立版本策略。
  • 合理的依赖管理:避免循环依赖,合理控制公共依赖。
  • 文档和说明:为每个包提供清晰的README和API文档,方便其他开发者使用。
  • 持续集成:集成自动化测试、代码质量检查和构建过程。
  • 代码审查:确保代码质量和遵循团队规范。
  • 定期清理:定期评估和删除不再使用的包。

其他Monorepo管理工具

除了Lerna,还有其他一些工具可以用于Monorepo管理,每个工具都有其特点和适用场景:

Yarn Workspaces

Yarn的Workspaces特性直接内置在包管理器中,无需额外的命令行工具。它允许在一个仓库中管理多个依赖,并自动解决跨包依赖。Workspaces适合那些主要使用Yarn作为包管理器的项目。

Rush (Pika)

Rush是Microsoft开发的一个Monorepo管理工具,专注于速度和可扩展性。Rush使用自定义的依赖解析算法,支持多项目和多包类型,包括TypeScript、JavaScript、C++等。Rush还提供了丰富的命令行工具和配置选项。

Nx ( Nrwl.io)

Nx 是一个开源的Monorepo管理工具,最初为Angular项目设计,但现在支持多种框架和技术,如React、Vue、Node.js等。Nx 提供了一整套工作流工具,包括代码生成、测试、性能分析和并行构建。它还支持微前端架构。

Monorepo的挑战

虽然Monorepo有许多优点,但也存在一些挑战:

  • 版本冲突:在大型项目中,管理不同包的版本可能会变得复杂。
  • 构建时间:随着项目规模的增长,构建和测试的时间可能显著增加。
  • 学习曲线:新成员可能需要更多时间来熟悉Monorepo的结构和工作流程。
  • 依赖管理:解决跨包依赖和避免循环依赖可能需要额外的注意。

选择Monorepo还是Polyrepo?

选择Monorepo还是Polyrepo(多仓库)取决于项目需求、团队规模和偏好。Monorepo适合需要频繁共享代码和组件的大型项目,而Polyrepo则适用于小项目或需要独立开发和发布的组件。

在做出决定时,应考虑以下因素:

  • 代码共享:如果项目间有很多共享代码,Monorepo可能是更好的选择。
  • 团队协作:如果团队需要紧密合作,Monorepo可以简化协同开发。
  • 构建时间:如果项目很大,Monorepo可能导致较长的构建时间。
  • 项目独立性:如果项目彼此独立,Polyrepo可能更合适。

这是一个从 https://juejin.cn/post/7368741477214437386 下的原始话题分离的讨论话题