likes
comments
collection
share

NodeJS 零散小项目难管理?试试这个 npm 提供的 'monorepo' - workspace

作者站长头像
站长
· 阅读数 9

最近写了的一些模块化,还有偏实验的文章,里面都有很多例子,大多都是提交 12 次,基本就结束整个项目的生命周期了,所以我就想着能不能把它们集中起来管理,简单搜索了一下,也许 monorepo 能解决我的问题

monorepo

Monorepo 的意思是在版本控制系统的单个代码库里包含了许多项目的代码。这些项目虽然有可能是相关的,但通常在逻辑上是独立的,并由不同的团队维护。

简单来说就是把几个项目丢到一个目录里面管理,但是这种属于‘简陋’的 monorepo,优秀的 monorepo 通常具有依赖管理、引用一致等功能,而对于我们的 npm 来说,就提供了 workspaces 来帮助我们实现 monorepo

Workspaces 指的是 npm cli 中的一组功能,它支持从单个根目录 package 中管理本地文件系统中的多个 package

广义的 monorepo 是可以包含 android/iOS/H5/Web 各种代码的,但是文章实现的是基于 Node.js 的狭义 monorepo

实践

我使用的 node@16.15.1 | npm@8.11.0,支持 workspaces 并且是 LTS 版本,关于 Node.jsNpm 的对应关系可以参考此链接

您可以在线查看源代码

本次的 monorepo 将集成我的以下几个项目

  1. pandoralink/vue2-start-with-vite
  2. pandoralink/vue-start-with-webpack
  3. pandoralink/react-start-with-vite
  4. pandoralink/react-start-with-webpack

初始化

先初始化一个项目

npm init

无论是已经开发中或者是刚创建的 Node.js 项目都可以使用下面这个命令去开启 workspaces

npm init [options] [your_package_name]
# Options
#   -w, --workspace=[your_package_name]
# Exampe
# npm init -w ./package/vue2-start-with-vite

添加的时候会走一遍类似 npm init 的流程,比如下面这种

NodeJS 零散小项目难管理?试试这个 npm 提供的 'monorepo' - workspace

已经有仓库的就没必要太在意初始化的 CLI 了,建议直接上默认选项,然后再复制就完事了

npm init -w ./package/your-porject-name -y
# 例如
# npm init -w ./package/react-start-with-webpack -y

添加完后会重写在项目根目录的 package.json,加上一个 workspaces 属性

{
  // ...
  "workspaces": [
    "package\\vue2-start-with-vite",
    "package\\vue-start-with-webpack",
    "package\\react-start-with-webpack",
    "package\\react-start-with-vite"
  ],
  // ...
}

项目结构如下

├── node_modules
├── package
|  ├── react-start-with-vite
|  ├── react-start-with-webpack
|  ├── vue-start-with-webpack
|  └── vue2-start-with-vite
├── package-lock.json
└── package.json

接下来就是喜闻乐见的依赖环节

添加/删除/更新依赖

worksapce 对库中某个项目的添加/删除更新依赖操作语法如下

npm [options] [dependencies_name] -w [project_name]
# Options
#   install [dependencies_name]
#   uninstall [dependencies_name]
#   update [dependencies_name]
# Example
# npm install vue@2.7.10 -w vue2-start-with-vite

前面部分和正常的单一代码库的操作一致,使用 workspaces 的时候后缀加上 -w [project_name] 就行

也可以子项目整个下载或者整个 monorepo 一起下载

npm install
npm install -w [project_name]

依赖关系

workspaces 全部的子项目的依赖都是在 root(根目录)的 node_modules 下统一管理的

依赖冲突

对于 monorepo 这种单一代码库来说,统一管理依赖必然会有冲突,而 npmworkspaces 的解决方案如下,对于进入根目录的 node_modules 的依赖会根据 workspaces 里面的顺序决定

{
  // ...
  "workspaces": [
    "package\\vue2-start-with-vite",
    "package\\vue-start-with-webpack",
    "package\\react-start-with-webpack",
    "package\\react-start-with-vite"
  ],
  // ...
}

而对于依赖冲突,就比如 vue2-start-with-vite 项目里用的是 vue@2.7.10,而 react-start-with-vite 项目里用到了 vue@3.2.45,那么下载的时候,根目录会是 vue@2.7.10,而子目录 react-start-with-vite 项目会开一个 node_modules 里面会下载 vue@3.2.45,如下

├── node_modules
|  ├── ...
|  ├── vue@2.7.10
|  └── ...
├── package
|  ├── react-start-with-vite
|  |  ├── node_modules
|  |  |  ├── ...
|  |  |  ├── vue@3.2.45
|  |  |  └── ...
|  |  └── package.json
|  └── ...
├── package-lock.json
└── package.json

这个解决思路非常好,但是有个缺点,比如 react-start-with-vite 项目突然不需要这个 vue@3.2.45uninstall 以后总是会留一堆“来过的痕迹”(非常丑陋)

NodeJS 零散小项目难管理?试试这个 npm 提供的 'monorepo' - workspace

运行

运行命令如下

npm run [script] [options] [your_package_name]
# Options
#   -w, --workspace=[your_package_name]
# Script
# dev/build/serve... 爱取啥取啥
# Exampe
# npm run start --workspace=react-start-with-webpack

其中 scriptpackage 里面的子项目的 package.json 中配置的 script 属性

monorepo 的优势

减少重复依赖

缩短启动时间

由于避免了重复安装依赖,再去启动一个项目会快很多,当你有了单一代码库(monorepo)的基础后,你再去开一个子项目,再去 npm install,会发现非常快,总是 up to date

D:\fe-project\engineering-learning>npm install -w react-start-with-webpack

up to date in 1s

而且对于初始化 monorepo 是不需要下载全部依赖的,可以单一下载某个项目

npm install [options] [package]
# Options
#   -w, --workspace=[your_package_name]
# Example
# npm install -w vue2-start-with-vite

不过对于有缓存机制的 node.js 来说,下载一次并不需要多久,更何况还有国内镜像

减少项目大小

通过减少重复依赖从而减少整个项目的大小,关于这个可能会认觉得我吹水,因为几乎很多介绍 monorepo 的文章曾经写过

谷歌拥有有史以来最大的代码库,每天有成百上千次提交,整个代码库超过 80 TB

我当下意识的以为使用 monorepo 技术是否会带来很大的硬盘压力,但实际上使用会发现,因为存在共同依赖的关系,多个项目的集成会避免下载重复的依赖,比如文章的这个例子

它是由四个子项目重新集成到一个 monorepo 库中,但是在大小上并不是 1 + 1 + 1 + 1 = 4,实际的大小减少了 46.83%(由于都是只有几次提交的项目,因此 .git 目录的大小可以忽略不计)

NodeJS 零散小项目难管理?试试这个 npm 提供的 'monorepo' - workspace

NodeJS 零散小项目难管理?试试这个 npm 提供的 'monorepo' - workspace

但是硬盘价格并不是很突出的今天,就算每个项目起步 1G 也并不是很贵

至于劣势,比如依赖冲突处理、团队推动成本、迁移成本、学习成本。。。(不过我认为最重要的还是背锅问题,毕竟老项目又不是不能用)

NodeJS 零散小项目难管理?试试这个 npm 提供的 'monorepo' - workspace

总结

这篇文章的 monorepo 只是一个跟风的玩具,从众多资料显示 monrepo 存在已久,并且众多团队曾经在单一代码库和多代码库中多次切换,很明显 monorepo 是一个很偏工程的技术,需要熟练的经验,需要充分理解它的理论。回到文章中实践的 monorepo,只是一次技术上的实践,笔者对它的大多数理论场景没有涉及,不过用来了解一下新技术也是不错的

参考资料