1、monorepo是什么
管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。
目前有很多方式可以搭建monorepo
-
yarn workspaces:Yarn提供的monorepo的依赖管理机制
-
lerna:一个开源的管理工具,用于管理包含多个软件包(package)的JavaScript 项目
-
pnpm workspaces :新一代 node 包管理器。它由 npm/yarn 衍生而来,但却解决了 npm/yarn 内部潜在的 bug,并且极大了地优化了性能,扩展了使用场景。
哪些大型开源项目使用monorepo:
Babel | create-react-app | react-router | vue3
这些项目的第一级目录的内容以脚手架为主,主要内容都在 packages 目录中、分多个 package 进行管理。
2、对比
-
lerna+monorepo
Lerna 是一个用来优化托管在git\npm上的多package代码库工作流的一个管理工具,可以让你在主项目下管理多个子项目,从而解决了多个包互相依赖,且发布时需要手动维护多个包的问题。
lerna是如何做到内部模块的软链和管理
未使用lerna之前,调试一个本地的 npm 模块包,使用npm link可以在系统目录下建立包软链。软链可以不需要发布,就可以使用本地包,很好的提高开发效率。
而在lerna 中可以直接进行模块的引入和调试。
-
lerna中软链的实现:
fs.symlinkSync(target,path,type)
首先创建名为 path 的链接,该链接指向 target。
type 参数仅在 Windows 上可用,在其他平台上则会被忽略。它可以被设置为 'dir'、 'file' 或 'junction'。
如果未设置 type 参数,则 Node.js 将会自动检测 target 的类型并使用 'file' 或 'dir'。如果 target 不存在,则将会使用 'file'。
Windows 上的连接点要求目标路径是绝对路径。当使用 'junction' 时, target 参数将会自动地标准化为绝对路径。
如何初始化项目:
安装lerna
npm i -g lerna
进入目录,执行初始化命令
lerna init --independent // 创建independent模式的项目
lerna有两种版本号管理模式:fixed 和 independent。fixed是默认模式,在这模式下所有包都使用lerna.json里的version字段值。independent模式是每个包使用独立的版本号。
代码结构如下:
创建子模块
lerna create [module-name]
lerna 提供了可以将子项目的依赖包提升到最顶层的方式 :
lerna clean // 删除每个子项目的 node_modules
lerna bootstrop --hoist // 将 packages 目录下的公共模块包抽离到最顶层
但是这种方式会有一个问题,不同版本号只会保留使用最多的版本,这种配置不太好,当项目中有些功能需要依赖老版本时,就会出现问题。
举例一些开源项目。
-
babel
-
facebook/jest
-
react
-
pnpm+monorepo
pnpm: Fast, disk space efficient package manager
特点:
创建非扁平化的 node_modules 文件夹
图中蓝框内的都是软链,比如babel-eslint,指向的是文件存储地址,文件真正存储的位置在.pnpm中。
这样的通过软链接的设计既保证了不会出现幽灵依赖的问题,同时也能兼容 node 的寻找模块方式。
幽灵依赖:在使用npm i 安装依赖时,有一些包的依赖包也会同时安装,但是这些包并未在package.json 中声明,这显然是不安全的,这种情况也被称为幽灵依赖。
pnpm 有个根目录,一般都是保存在 user/.pnpm-store 下,pnpm 通过硬链接的方式保证了相同的包不会被重复下载。
继续查看下babel-eslint中的index.js文件信息:
index.js inode节点为 13034667887 并且有4个相同的硬链接。也就是说index.js 是被复用的。
安装时一定要保证node版本
选择了node v14.19 pnpm v7.3.0
开始安装的pnpm v7.3.0 一直提示command not found: pnmp => node版本需要至少v14.19
功能比较:
功能 | pnpm:v7+ | Yarn | npm |
---|---|---|---|
工作空间支持(monorepo) | ✔️ | ✔️ | ✔️ |
隔离的 node_modules | ✔️ - 默认 | ✔️ | ❌ |
提升的 node_modules | ✔️ | ✔️ | ✔️ - 默认 |
自动安装 peers | ✔️ - 通过 auto-install-peers=true | ❌ | ✔️ |
Plug'n'Play | ✔️ | ✔️ - 默认 | ❌ |
零安装 | ❌ | ✔️ | ❌ |
修补依赖项 | ✔️ | ✔️ | ❌ |
管理 Node.js 版本 | ✔️ | ❌ | ❌ |
有锁文件 | ✔️ - pnpm-lock.yaml | ✔️ - yarn.lock | ✔️ - package-lock.json |
支持覆盖 | ✔️ | ✔️ - 通过 resolutions | ✔️ |
内容可寻址存储 | ✔️ | ❌ | ❌ |
动态包执行 | ✔️ - 通过 pnpm dlx | ✔️ - 通过 yarn dlx | ✔️ - 通过 npx |
Side-effects cache | ✔️ | ❌ | ❌ |