大家好,我是你们的老朋友,今天咱们来聊聊大型 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.json
和 packages
目录。 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 客户端。 可以是
npm
、yarn
或pnpm
。 - 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. 发布流程
- 确保你的代码已经提交到 Git 仓库。
- 运行
lerna bootstrap
安装所有依赖。 - 运行
lerna run build
构建所有 package。 - 运行
lerna changed
检查哪些 package 需要发布。 - 运行
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 管理工具,可以帮助你管理依赖、构建和发布流程。 选择哪个工具取决于你的具体需求和项目规模。
希望今天的分享对你有所帮助! 如果你有任何问题,欢迎留言交流。 咱们下期再见!