探讨如何在一个大型 Vue Monorepo 项目中,利用工具 (如 Lerna, Nx) 管理多包依赖、构建和发布流程。

大家好,我是你们的老朋友,今天咱们来聊聊大型 Vue Monorepo 项目的管理之道。想想看,你手头有个项目,里面包含了十几个甚至几十个 Vue 组件库、工具函数库、甚至独立的 Vue 应用,它们之间互相依赖,版本管理混乱,构建发布更是让人头疼,是不是感觉头发都要掉光了?别慌,今天咱们就来拯救你的发际线!

Monorepo,顾名思义,就是把多个项目放在同一个代码仓库中进行管理。 听起来有点反直觉,但它在大型项目中优势巨大,比如代码复用、依赖管理、原子性变更等等。 关键在于如何有效地管理这些相互依赖的包。

今天,我们重点讲讲如何利用 Lerna 和 Nx 这两大利器,来管理 Vue Monorepo 的依赖、构建和发布流程。

Lerna:Monorepo 的老牌管家

Lerna 诞生较早,专注于解决 Monorepo 的版本管理和发布问题。 它的核心思想是将 Monorepo 中的每个包都视为一个独立的 npm 包,并允许你独立地发布它们。

1. Lerna 的基本概念和安装

  • Packages: Monorepo 中的每个独立模块或组件库。
  • lerna.json: Lerna 的配置文件,定义了 Monorepo 的结构和行为。
  • lerna publish: Lerna 的发布命令,用于自动发布有更新的包。

首先,我们需要全局安装 Lerna:

npm install -g lerna

然后,在你的项目根目录下初始化 Lerna:

lerna init

这会在你的项目根目录下生成 lerna.jsonpackages 目录。 packages 目录用于存放你的各个 package。

2. Lerna.json 的配置

lerna.json 是 Lerna 的灵魂,我们来了解一下它的重要配置项:

{
  "packages": [
    "packages/*"
  ],
  "version": "independent",
  "npmClient": "npm",
  "useWorkspaces": true,
  "command": {
    "publish": {
      "conventionalCommits": true,
      "message": "chore(release): publish %s"
    }
  }
}
  • packages: 指定 Monorepo 中 package 的位置。 默认是 packages/*,意味着 packages 目录下所有的子目录都被视为一个 package。
  • version: 指定 Monorepo 的版本管理模式。 independent 意味着每个 package 都有自己的版本号,可以独立升级。 还有一种模式是 fixed (或 locked),所有 package 使用同一个版本号。 推荐使用 independent,更灵活。
  • npmClient: 指定使用的 npm 客户端。 可以是 npmyarnpnpm
  • useWorkspaces: 如果使用 yarn 或 pnpm workspaces,需要设置为 true。 这样 Lerna 会利用 workspaces 的特性来加速依赖安装和构建。
  • command.publish.conventionalCommits: 如果使用 Conventional Commits 规范,可以设置为 true。 Lerna 会根据 commit 信息自动生成版本号。
  • command.publish.message: 自定义发布 commit 的信息。

3. 创建 Vue Package

packages 目录下,创建你的 Vue package。 比如,我们创建一个名为 vue-component-library 的组件库:

mkdir packages/vue-component-library
cd packages/vue-component-library
npm init -y
npm install vue --save

packages/vue-component-library 目录下,创建一个 src 目录,并在其中编写你的 Vue 组件。例如,创建一个简单的按钮组件 MyButton.vue

<template>
  <button class="my-button">{{ label }}</button>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    label: {
      type: String,
      default: 'Click me'
    }
  }
}
</script>

<style scoped>
.my-button {
  background-color: #4CAF50;
  border: none;
  color: white;
  padding: 15px 32px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  cursor: pointer;
}
</style>

然后,创建一个 index.js 文件,用于导出你的组件:

import MyButton from './src/MyButton.vue';

export {
  MyButton
};

最后,在 packages/vue-component-library/package.json 中,配置组件库的入口文件:

{
  "name": "@your-org/vue-component-library",
  "version": "0.1.0",
  "description": "A Vue component library",
  "main": "index.js",
  "module": "index.js", // For bundlers like Webpack
  "files": [
    "src",
    "index.js"
  ],
  "scripts": {
    "build": "vue-cli-service build --target lib --name vue-component-library index.js"
  },
  "dependencies": {
    "vue": "^3.0.0"
  },
  "devDependencies": {
    "@vue/cli-service": "^5.0.0"
  }
}

关键点:

  • name: Package 的名称必须是唯一的,通常使用 scoped package 的形式 @your-org/package-name
  • main: 指定 CommonJS 模块的入口文件。
  • module: 指定 ES 模块的入口文件,供 Webpack 等打包工具使用。
  • files: 指定发布到 npm 的文件。
  • scripts.build: 构建组件库的脚本。这里使用 Vue CLI 的 build --target lib 命令,将组件库打包成一个独立的库。

4. 处理依赖关系

如果你的 package 之间存在依赖关系,需要在 package.json 中声明。 例如,我们创建一个名为 vue-app 的 Vue 应用,它依赖于 vue-component-library

mkdir packages/vue-app
cd packages/vue-app
npm init -y
npm install vue --save

packages/vue-app/package.json 中,添加对 vue-component-library 的依赖:

{
  "name": "@your-org/vue-app",
  "version": "0.1.0",
  "description": "A Vue application",
  "dependencies": {
    "@your-org/vue-component-library": "workspace:*"
  }
}

关键点:

  • workspace:*: 这是 yarn 或 pnpm workspaces 的语法,告诉 npm 客户端从 Monorepo 内部解析依赖。 这样,vue-app 就可以直接使用 vue-component-library 的代码,而无需从 npm 下载。

5. Lerna 的常用命令

  • lerna bootstrap: 安装所有 package 的依赖。 如果使用了 yarn 或 pnpm workspaces,Lerna 会自动利用 workspaces 的特性来加速依赖安装。
  • lerna run : 在所有 package 中运行指定的 npm script。 例如,lerna run build 会在所有 package 中运行 npm run build
  • lerna changed: 列出自上次发布以来发生更改的 package。
  • lerna publish: 发布有更新的 package。 Lerna 会自动分析 commit 信息,确定哪些 package 需要发布,并自动更新版本号、生成 changelog、打 tag、发布到 npm。

6. 发布流程

  1. 确保你的代码已经提交到 Git 仓库。
  2. 运行 lerna bootstrap 安装所有依赖。
  3. 运行 lerna run build 构建所有 package。
  4. 运行 lerna changed 检查哪些 package 需要发布。
  5. 运行 lerna publish 发布 package。

Lerna 会自动完成以下步骤:

  • 分析 commit 信息,确定需要发布的 package。
  • 根据 Conventional Commits 规范,自动更新版本号。
  • 生成 changelog。
  • 打 tag。
  • 发布到 npm。

Nx:Monorepo 的全能选手

Nx 不仅仅是一个版本管理工具,它更是一个强大的构建系统和开发工具。 它可以帮你管理 Monorepo 中的任务依赖、缓存构建结果、并行执行任务,从而显著提升开发效率。

1. Nx 的基本概念和安装

  • Workspace: Nx 的核心概念,指包含多个项目的代码仓库。
  • Project: Workspace 中的每个独立模块或应用。
  • Task: 对 Project 执行的操作,比如构建、测试、lint 等。
  • nx.json: Nx 的配置文件,定义了 Workspace 的结构和行为。
  • Nx Console: 一个 VS Code 插件,提供图形化界面来管理 Nx Workspace。

首先,我们需要全局安装 Nx CLI:

npm install -g create-nx-workspace

然后,创建一个新的 Nx Workspace:

create-nx-workspace my-monorepo --preset=npm --nx-cloud=false

这里我们选择了 npm 作为包管理器,并禁用了 Nx Cloud。

2. Nx Workspace 的结构

Nx Workspace 的结构如下:

my-monorepo/
├── apps/       # 用于存放 Vue 应用
├── libs/       # 用于存放 Vue 组件库、工具函数库
├── tools/      # 用于存放自定义脚本
├── nx.json     # Nx 的配置文件
├── package.json  # Workspace 的 package.json
├── tsconfig.base.json # 基础的 TypeScript 配置文件
└── ...

3. 创建 Vue Project

使用 Nx CLI 创建 Vue Project。 比如,我们创建一个名为 vue-component-library 的组件库:

nx generate @nx/vue:library vue-component-library

这会在 libs 目录下创建一个 vue-component-library 目录,并生成相关的配置文件。

同样,我们可以创建一个 Vue 应用 vue-app

nx generate @nx/vue:application vue-app

这会在 apps 目录下创建一个 vue-app 目录。

4. Nx.json 的配置

nx.json 是 Nx 的灵魂,我们来了解一下它的重要配置项:

{
  "npmScope": "your-org",
  "affected": {
    "defaultBase": "main"
  },
  "implicitDependencies": {
    "package.json": {
      "dependencies": "*",
      "devDependencies": "*"
    },
    ".eslintrc.json": "*"
  },
  "tasksRunnerOptions": {
    "default": {
      "runner": "@nrwl/nx-cloud",
      "options": {
        "cacheableOperations": [
          "build",
          "lint",
          "test",
          "e2e"
        ],
        "accessToken": "..."
      }
    }
  },
  "targetDefaults": {
    "build": {
      "dependsOn": [
        "^build"
      ],
      "inputs": [
        "default",
        "{workspaceRoot}/babel.config.json",
        "{workspaceRoot}/.swcrc",
        "{workspaceRoot}/tsconfig.base.json"
      ]
    }
  },
  "namedInputs": {
    "default": [
      "{projectRoot}/**/*",
      "sharedGlobals"
    ],
    "sharedGlobals": []
  },
  "generators": {
    "@nx/vue": {
      "application": {
        "unitTestRunner": "vitest",
        "e2eTestRunner": "cypress"
      },
      "library": {
        "unitTestRunner": "vitest"
      }
    },
      "@nx/eslint:configuration": {
        "packageJson": ["devDependencies"]
    }
  },
  "defaultProject": "vue-app"
}
  • npmScope: 指定 npm scope。
  • affected.defaultBase: 指定默认的 base branch,用于确定受影响的项目。
  • tasksRunnerOptions: 配置任务运行器。 Nx Cloud 是 Nx 官方提供的云端构建缓存服务,可以显著提升构建速度。
  • targetDefaults: 配置 target 的默认选项。 例如,build.dependsOn 指定了 build target 依赖于哪些 target。
  • namedInputs: 定义命名的输入,用于缓存构建结果。
  • generators: 定义了不同生成器的默认配置。

5. 处理依赖关系

Nx 会自动分析项目之间的依赖关系。 如果 vue-app 依赖于 vue-component-library,Nx 会自动将 vue-component-library 添加到 vue-app 的依赖列表中。

你也可以手动添加依赖关系:

nx generate @nx/workspace:add-dependency --project=vue-app --target=vue-component-library

6. Nx 的常用命令

  • nx build : 构建指定的项目。 Nx 会自动分析依赖关系,并并行构建所有依赖的项目。
  • nx test : 运行指定项目的测试。
  • nx lint : Lint 指定项目的代码。
  • nx serve : 启动指定项目的开发服务器。
  • nx graph: 生成项目依赖关系图。 这个命令非常有用,可以帮助你理解 Monorepo 的结构。
  • nx affected:build: 构建受影响的项目。 Nx 会根据代码变更,自动确定哪些项目需要重新构建。
  • nx affected:test: 运行受影响的项目的测试。
  • nx affected:lint: Lint 受影响的项目的代码。

7. Nx 的优势

  • 强大的构建系统: Nx 提供强大的构建系统,可以自动分析依赖关系、缓存构建结果、并行执行任务,从而显著提升构建速度。
  • 代码生成器: Nx 提供丰富的代码生成器,可以快速创建项目、组件、服务等。
  • 代码检查: Nx 提供代码检查功能,可以帮助你保持代码风格一致。
  • 可视化工具: Nx 提供可视化工具,可以帮助你理解 Monorepo 的结构和依赖关系。
  • Nx Cloud: Nx Cloud 提供云端构建缓存服务,可以进一步提升构建速度。

Lerna vs Nx:如何选择?

特性 Lerna Nx
核心功能 版本管理和发布 构建系统、开发工具、版本管理
学习曲线 简单易用 稍陡峭,需要理解 Nx 的概念和配置
构建系统 无内置构建系统 强大的构建系统,支持缓存、并行执行
代码生成器 无内置代码生成器 丰富的代码生成器
适用场景 主要关注版本管理和发布的 Monorepo 需要强大构建系统和开发工具的大型 Monorepo

简单来说,如果你的 Monorepo 主要关注版本管理和发布,Lerna 是一个不错的选择。 如果你的 Monorepo 需要强大的构建系统和开发工具,Nx 则是更佳的选择。

当然,你也可以将 Lerna 和 Nx 结合使用。 使用 Nx 管理构建和测试,使用 Lerna 管理版本和发布。

总结

Monorepo 是管理大型项目的有效方式。 Lerna 和 Nx 都是强大的 Monorepo 管理工具,可以帮助你管理依赖、构建和发布流程。 选择哪个工具取决于你的具体需求和项目规模。

希望今天的分享对你有所帮助! 如果你有任何问题,欢迎留言交流。 咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注