在现代的软件开发中,多包项目的管理是一个常见的需求。随着项目规模的不断扩大,将一个大型项目拆分成多个小包可以提高代码的可维护性和复用性。今天咱们就来聊聊使用 lerna 和 yarn workspaces 来管理 npm 多包项目的方案。

一、背景知识

1.1 npm

npm 是 Node.js 的包管理工具,它允许开发者共享和复用代码。通过 npm,我们可以轻松地安装、更新和管理项目依赖。例如,在一个 Node.js 项目中,我们可以使用以下命令安装一个包:

# 安装 express 包
npm install express

1.2 monorepo

monorepo 是一种将多个项目或包放在同一个代码仓库中的管理方式。与传统的多仓库管理方式相比,monorepo 可以更方便地进行代码共享、版本管理和跨项目协作。比如,一个公司的多个前端组件库可以放在同一个 monorepo 中,这样团队成员可以更方便地进行开发和维护。

1.3 lerna

lerna 是一个用于管理具有多个包的 JavaScript 项目的工具。它可以帮助我们自动化处理多包项目中的一些常见任务,如版本管理、发布等。例如,lerna 可以自动更新所有包的版本号,并将它们发布到 npm 上。

1.4 yarn workspaces

yarn workspaces 是 Yarn 提供的一个功能,它允许我们在一个项目中管理多个包。通过 yarn workspaces,我们可以将多个包的依赖集中管理,避免重复安装相同的依赖,从而节省磁盘空间和安装时间。

二、应用场景

2.1 大型项目拆分

当一个项目变得越来越大时,代码的可维护性会受到影响。这时,我们可以将项目拆分成多个小包,每个包负责不同的功能模块。例如,一个电商网站的前端项目可以拆分成商品展示、购物车、用户登录等多个包,使用 lerna 和 yarn workspaces 可以方便地管理这些包。

2.2 代码复用

在多个项目中可能会有一些通用的代码,如工具函数、组件库等。将这些通用代码提取到单独的包中,并使用 monorepo 管理,可以方便地在不同项目中复用这些代码。比如,一个公司的多个前端项目可以共享同一个 UI 组件库。

2.3 团队协作

在一个大型团队中,不同的成员可能负责不同的包。使用 monorepo 可以让团队成员更方便地进行协作,避免了多仓库管理中可能出现的代码不一致问题。例如,一个前端团队可以在同一个 monorepo 中开发不同的前端组件。

三、搭建项目

3.1 初始化项目

首先,我们需要创建一个新的项目文件夹,并初始化一个新的 npm 项目:

# 创建项目文件夹
mkdir my-monorepo
# 进入项目文件夹
cd my-monorepo
# 初始化 npm 项目
npm init -y

3.2 配置 yarn workspaces

在项目根目录下创建一个 package.json 文件,并添加以下内容:

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

上述配置表示我们将在 packages 文件夹下管理多个包。

3.3 创建子包

packages 文件夹下创建两个子包:package-apackage-b

# 创建 packages 文件夹
mkdir packages
# 进入 packages 文件夹
cd packages
# 创建 package-a 文件夹
mkdir package-a
# 进入 package-a 文件夹
cd package-a
# 初始化 npm 项目
npm init -y
# 返回 packages 文件夹
cd ..
# 创建 package-b 文件夹
mkdir package-b
# 进入 package-b 文件夹
cd package-b
# 初始化 npm 项目
npm init -y

3.4 安装 lerna

在项目根目录下安装 lerna:

# 安装 lerna
npm install lerna --save-dev

3.5 初始化 lerna

在项目根目录下初始化 lerna:

# 初始化 lerna
npx lerna init

这将在项目根目录下创建一个 lerna.json 文件,用于配置 lerna。

四、示例代码

4.1 在 package-a 中编写代码

packages/package-a 文件夹下创建一个 index.js 文件,内容如下:

// packages/package-a/index.js
// 定义一个函数,用于返回两个数的和
function add(a, b) {
  return a + b;
}

module.exports = {
  add
};

4.2 在 package-b 中使用 package-a

packages/package-b 文件夹下创建一个 index.js 文件,内容如下:

// packages/package-b/index.js
// 引入 package-a 中的 add 函数
const { add } = require('package-a');

// 调用 add 函数
const result = add(1, 2);
console.log(result);

4.3 安装依赖

在项目根目录下运行以下命令,安装所有包的依赖:

# 安装所有包的依赖
yarn install

4.4 链接包

在项目根目录下运行以下命令,将 package-a 链接到 package-b 中:

# 链接 package-a 到 package-b
npx lerna add package-a --scope=package-b

4.5 运行代码

packages/package-b 文件夹下运行以下命令,执行 index.js 文件:

# 运行 index.js 文件
node index.js

如果一切正常,你将看到输出结果 3

五、技术优缺点

5.1 优点

5.1.1 代码共享方便

在 monorepo 中,不同的包可以方便地共享代码,避免了代码重复。例如,在我们的示例中,package-b 可以直接使用 package-a 中的代码。

5.1.2 依赖管理高效

yarn workspaces 可以将多个包的依赖集中管理,避免了重复安装相同的依赖,节省了磁盘空间和安装时间。

5.1.3 版本管理统一

lerna 可以帮助我们统一管理多个包的版本号,避免了版本不一致的问题。例如,lerna 可以自动更新所有包的版本号,并将它们发布到 npm 上。

5.1.4 团队协作友好

monorepo 可以让团队成员更方便地进行协作,避免了多仓库管理中可能出现的代码不一致问题。例如,一个前端团队可以在同一个 monorepo 中开发不同的前端组件。

5.2 缺点

5.2.1 仓库体积增大

由于所有的包都放在同一个仓库中,仓库的体积可能会变得很大,这会影响克隆和拉取代码的速度。

5.2.2 复杂度增加

随着包的数量增加,项目的复杂度也会增加,管理和维护的难度也会相应提高。例如,在一个包含大量包的 monorepo 中,查找和修改代码可能会变得更加困难。

5.2.3 权限管理困难

在一个大型团队中,不同的成员可能只需要访问部分包。在 monorepo 中,权限管理可能会变得更加困难,因为所有的包都在同一个仓库中。

六、注意事项

6.1 版本管理

在使用 lerna 进行版本管理时,需要注意版本号的更新规则。lerna 提供了两种版本管理模式:固定模式和独立模式。固定模式下,所有包的版本号都会保持一致;独立模式下,每个包的版本号可以独立更新。

6.2 依赖冲突

由于多个包共享同一个依赖管理环境,可能会出现依赖冲突的问题。在添加和更新依赖时,需要仔细检查依赖的版本号,避免出现冲突。

6.3 代码隔离

虽然 monorepo 方便了代码共享,但也需要注意代码的隔离。不同的包应该有明确的职责和边界,避免出现代码耦合度过高的问题。

七、文章总结

使用 lerna 和 yarn workspaces 管理 npm 多包项目是一种高效的方式,它可以提高代码的可维护性和复用性,方便团队协作。通过本文的介绍,我们了解了相关的背景知识、应用场景、搭建项目的步骤、示例代码以及技术的优缺点和注意事项。在实际应用中,我们可以根据项目的规模和需求来选择是否使用这种管理方案。如果项目规模较大,且需要频繁进行代码共享和协作,那么使用 lerna 和 yarn workspaces 管理 monorepo 是一个不错的选择。