使用Lerna + Yarn Workspace管理Monorepo项目

2024-03-09 01:28

本文主要是介绍使用Lerna + Yarn Workspace管理Monorepo项目,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.前言

通常,我们会根据自身业务的实际情况,将通用的组件、逻辑等提取成NPM包,方便以后复用。但这些提取出来的NPM包可能互相之间存在依赖,如果仍然采用 Multirepo 的形式进行管理,则在包的版本管理、依赖管理、调试等诸多方面存在不便。
Monorepo 能很好的解决上述问题,为更加方便的使用 Monorepo 来管理我们的项目,我们需要一些趁手的工具,Lerna + Yarn Workspace 的组合就是这样一件优秀的工具。然而 Lerna 中途经历过较大的改动,其文档也不是那么容易看明白,中文材料大多使用较老版本的 Lerna,使用上和较新版本存在差异。
本文将介绍较新版本的 Lerna 如何结合 Yarn Workspace 管理 Monorepo 项目,后续操作在 Lerna V7 和 V8 中经过实践。

2.Lerna + Yarn Workspace

其实除了 Lerna + Yarn Workspace 的方案外,还有其他方案也可以帮助我们管理 Monorepo 项目,比如 pnpm 等,但我选择 Lerna + Yarn Workspace 一方面是由于习惯了使用 yarn,只要引入 Lerna 即可,另一方面公司的基建对 yarn 也有很好的支持,所以选择了 Lerna + Yarn Workspace。
在使用 Lerna + Yarn Workspace 时,两者的分工有所不同:

  • Yarn 负责依赖管理,在 install 时将子包相同的依赖安装到根目录下的 node_modules 中,避免了在子包中重复安装。同时 Yarn 也可以处理子包之间的相互依赖关系,使用软链的方式连接各个子包。
  • Lerna 则负责子包的发布,包括版本号更新、依赖更新、发布等。

3.入门实践

3.1.初始化

3.1.1.项目初始化

首先你需要初始化 Monorepo 项目,新建一个目录 my-monorepo-project,进入到目录中执行:

yarn init

这一步完成后,项目根目录下会出现 package.json,这里需要将 private 字段设为 true。

3.1.2.Workspaces 初始化

接下来进行 Workspaces 初始化,在根目录新建 packages 文件夹,并在 packages.json 中新增 workspaces 配置:

"workspaces": ["packages/*"
]

workspaces 字段定义了一个包含多个路径的数组,这些路径指向了项目中的各个子包的位置。如上配置就是将 packages 目录下的所有项目都看做是子包。
这一步完成之后,就可以在根目录下执行 yarn 安装依赖了,假设我们的项目目录如下:

/my-monorepo-project
|-- packages
|   |-- package1
|   |-- package2
|   |-- package3
|-- package.json
|-- yarn.lock

其中 package1 依赖了 package2,则 yarn 之后,my-monorepo-project 的根目录下会出现 node_modules 文件夹,在 node_modules 查看各个子包对应的目录,会发现它们是以软链的形式链接到各自的源码目录中。

值得注意的是,此时就算 package1 的 package.json 中没有声明对 package2 的依赖,但也可以直接在 package1 中引用 package2 了。但当我们发布后,由于 package1 没有声明对 package2 的依赖,用户在安装 package1 时不会同时安装 package2,就会造成运行异常。正确的做法仍然需要在 package1 的 package.json 中声明对 package2 的依赖,不过不要通过 yarn workspace package1 add package2的方式添加依赖,这样 package2 会从 npm 上下载,而不是链接到本地源码。

3.1.3.Lerna初始化

现在就可以引入 Lerna 了,在根目录中继续执行:

yarn add lerna -D 
npx lerna init 

如此一来,在根目录下就会新增 lerna.json 文件,其内容是:

{"$schema": "node_modules/lerna/schemas/lerna-schema.json","version": "independent","npmClient": "yarn"
}

在较早版本的 Lerna 生成的 lerna.json 文件中,还需要设置 useWorkspaces 字段,不过Lerna 7 和 8 中已经不用设置该字段了。
另外需要注意 version 字段的值。version 可以设置为具体的版本号,比如 0.0.1,此时 Lerna 将采用固定模式(Fixed/Locked mode)管理子包版本,即有的子包将共享同一个版本号。Lerna 默认采用该方式,version 初始值为 0.0.0。
也可以将 version 设置为 independent,表示每个子包都可以独立地管理自己的版本号(Independent mode),当发布时 Lerna 会让你为子包选择版本号。由于我是将已有的项目改造为 Monorepo 项目,各子包已经独立发版很长时间了,所以将 version 改成了 independent。

3.2.开发

完成初始化后,我们就可以开发我们的子包了。在开发过程中,我们可能需要添加第三方依赖,也有可能需要运行子包的 scripts 脚本。我们来看看如何执行这些常见的操作。

3.2.1.添加依赖

如果需要添加第三方依赖,可以将依赖添加到 workspaces 根目录:

# 在任何目录执行
yarn add <依赖名> -W# 或者在根目录中执行
yarn add <依赖名>

这样依赖会被安装到根目录的 node_modules 中,所有子包都可以引用。

如果需要为某个子包单独添加某个依赖,可以执行:

# 在任何目录执行
yarn workspace <子包名> add <依赖名># 或者在进入子包对应的目录中执行
yarn add <依赖名>

这样依赖只会被会被安装到子包对应目录下的 node_modules 中,其他子包不能直接引用。注意子包名指的是子包 package.json 中 name 字段的值,不是子包的目录名。

我们可以通过如下命令查看子包间的依赖关系,防止日后理不清子包的依赖关系:

yarn workspaces info

3.2.2.运行script

如果需要执行某个子包中的脚本,可以执行:

yarn workspace <子包名> run build

这样就会运行子包的 build 命令。

3.3.发布

整个开发过程都是由 Yarn 进行管理,但 Yarn Workspace 不具备发布的能力,这时就需要借助 Lerna 的能力了。

3.3.1.检查更新

需要检查自上次发布以来在 Git 中已经提交但尚未发布的修改,执行:

npx lerna changed

该命令的结果形如:

# 没有未发布的修改
lerna notice cli v7.4.2
lerna info versioning independent
lerna notice Current HEAD is already released, skipping change detection.
lerna info No changed packages found# 有未发布的修改
lerna notice cli v7.4.2
lerna info versioning independent
lerna info Looking for changed packages since package1@0.0.1
package1
package2

该命令的结果不仅包含发生修改的子包本身,还包括依赖了该子包的其他子包。比如 package2 发生修改,则依赖它的 package1 也会被识别为需要发布,因为需要修改 package1 依赖的 package2 的版本。

3.3.2.更新版本号

之前说过,在 lerna.json 中配置 version 为 independent 表示每个子包可以独立地更新版本号。假设我们修改了 package1 的代码,首先需要提交修改,否则发布时会因为没有提交记录而被 Lerna 卡住。
接下来需要更新版本号,这一步不需要直接修改 package.json,而是由 Lerna 帮我们修改,执行:

npx lerna version --no-private

–no-private 表示不更新私有包,因为他们不会被发布。命令执行之后,Lerna 会自动识别需要更新版本的子包(即 lerna changed 的结果),并向用户确认下一个版本号是多少。用户依次确认所有需要更新的子包的新版本号后,Lerna 会打 Tag,并push。
这一步不仅会修改 package.json 中的 version,也会修改依赖的版本。比如 package2 更新了,则在 package1 的 package.json 中,不仅自身的版本号会更新,其依赖的 package1 的版本也会跟着改变。

3.3.3.发布

发布就很简单了,执行:

npx lerna publish from-package

即可将更新的子包发布到NPM。

4.踩坑

我是将已经独立发版一段时间的好几个子包改造为使用 Monorepo 架构进行管理。由于 Lerna 基于 Tag 判断有哪些子包需要发布,由于之前发布子包时没有打Tag,所以执行 npx lerna changed会提示 lerna success found 9 packages ready to publish,意思是所有的子包都要发布。但执行 npx lerna publish from-package时由于本地包和 npm 上的版本相同,会提示 No unpublished release found,意思是没有需要发布的包。
为了解决上述问题,需要补一下Tag,具体方法是执行 npx lerna version --no-private,Lerna 会让填写每个子包的新版本号,没有修改的子包版本号可以保持不变。执行完毕后再执行 npx lerna changed会提示 Current HEAD is already released, skipping change detection... No changed packages found,即达成目的。
随后执行 npx lerna publish from-package,发布需要更新的子包即可。如果没有要发布的子包,这一步也可以不执行。

5.参考

  • Yarn文档
  • Yarn Workspaces
  • Lerna文档
  • Lerna文档翻译
  • Lerna 备忘清单
  • Lerna + Yarn workspace 使用总结

这篇关于使用Lerna + Yarn Workspace管理Monorepo项目的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/789099

相关文章

eclipse如何运行springboot项目

《eclipse如何运行springboot项目》:本文主要介绍eclipse如何运行springboot项目问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目js录当在eclipse启动spring boot项目时出现问题解决办法1.通过cmd命令行2.在ecl

Mysql中的用户管理实践

《Mysql中的用户管理实践》:本文主要介绍Mysql中的用户管理实践,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录13. 用户管理13.1 用户 13.1.1 用户信息 13.1.2 创建用户 13.1.3 删除用户 13.1.4 修改用户

使用Python实现网页表格转换为markdown

《使用Python实现网页表格转换为markdown》在日常工作中,我们经常需要从网页上复制表格数据,并将其转换成Markdown格式,本文将使用Python编写一个网页表格转Markdown工具,需... 在日常工作中,我们经常需要从网页上复制表格数据,并将其转换成Markdown格式,以便在文档、邮件或

Python使用pynput模拟实现键盘自动输入工具

《Python使用pynput模拟实现键盘自动输入工具》在日常办公和软件开发中,我们经常需要处理大量重复的文本输入工作,所以本文就来和大家介绍一款使用Python的PyQt5库结合pynput键盘控制... 目录概述:当自动化遇上可视化功能全景图核心功能矩阵技术栈深度效果展示使用教程四步操作指南核心代码解析

使用Python获取JS加载的数据的多种实现方法

《使用Python获取JS加载的数据的多种实现方法》在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段,许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取... 目录引言一、动态 网页与js加载数据的原理二、python爬取JS加载数据的方法(一)分析网络请求1

SpringCloud使用Nacos 配置中心实现配置自动刷新功能使用

《SpringCloud使用Nacos配置中心实现配置自动刷新功能使用》SpringCloud项目中使用Nacos作为配置中心可以方便开发及运维人员随时查看配置信息,及配置共享,并且Nacos支持配... 目录前言一、Nacos中集中配置方式?二、使用步骤1.使用$Value 注解2.使用@Configur

Mac备忘录怎么导出/备份和云同步? Mac备忘录使用技巧

《Mac备忘录怎么导出/备份和云同步?Mac备忘录使用技巧》备忘录作为iOS里简单而又不可或缺的一个系统应用,上手容易,可以满足我们日常生活中各种记录的需求,今天我们就来看看Mac备忘录的导出、... 「备忘录」是 MAC 上的一款常用应用,它可以帮助我们捕捉灵感、记录待办事项或保存重要信息。为了便于在不同

如何Python使用设置word的页边距

《如何Python使用设置word的页边距》在编写或处理Word文档的过程中,页边距是一个不可忽视的排版要素,本文将介绍如何使用Python设置Word文档中各个节的页边距,需要的可以参考下... 目录操作步骤代码示例页边距单位说明应用场景与高级用China编程途小结在编写或处理Word文档的过程中,页边距是一个

SpringBoot项目Web拦截器使用的多种方式

《SpringBoot项目Web拦截器使用的多种方式》在SpringBoot应用中,Web拦截器(Interceptor)是一种用于在请求处理的不同阶段执行自定义逻辑的机制,下面给大家介绍Sprin... 目录一、实现 HandlerInterceptor 接口1、创建HandlerInterceptor实

使用JavaConfig配置Spring的流程步骤

《使用JavaConfig配置Spring的流程步骤》JavaConfig是Spring框架提供的一种基于Java的配置方式,它通过使用@Configuration注解标记的类来替代传统的XML配置文... 目录一、什么是 JavaConfig?1. 核心注解2. 与 XML 配置的对比二、JavaConf