Monorepo

为什么要用 Monorepo?

Lerna

Lerna 是基于对包管理器npm/yarn的封装是来实现的,它无法有效地控制node_modules的内容:

  • Lerna 会为每一个package都调用yarn install,这导致了额外的开销,因为每一个package.json是独立的且它们之间无法共享依赖。这也导致了在这些node_modules文件夹里产生了大量的重复依赖,因为这些package经常会使用相同的第三方包。
  • 安装完成之后,Lerna 会手动地在相互引用的package之间创建链接link。这将在这些node_module内部引入连包管理器都无法感知到的不一致性,因此在一个包内部运行yarn install,可能会破坏 Lerna 管理的元结构(meta structure)。

Yarn Workspaces

参考文档:

Yarn 的原生 Workspaces 功能,通过消除跨 Workspaces 的依赖包重复问题,能够让安装依赖变得更快更轻量。Yarn 可以在相互依赖的 Workspace 创建符号链接symlinks,确保所有目录的一致性和正确性。

Workspaces 的依赖安装

  • 每个 Workspace 里安装的依赖,都会被提升到 Monorepo 根目录下的node_modules目录里
  • 若某个 Workspace 里依赖的包 PackageA 与根目录里依赖的包 PackageA 的版本不兼容,则会在根目录下和该 Workspace 下分别安装不同版本的 PackageA
  • 根目录下的node_modules目录里涉及到 Workspace 的依赖,都会以符号链接symlink的方式链接到该 Monorepo 下对应的 Workspace 文件夹
  • 每个 Workspace 没有它自己的yarn.lock文件,只有根目录存在yarn.lock文件,包含了整个 Monorepo 下所有的 Workspace 的依赖
  • 当在某个 Workspace 下执行安装依赖命令时,该 Workspace 下的package.json会增加一条依赖记录,且 Monorepo 根目录下的yarn.lock会增加一条依赖记录

Workspaces 的发布问题

通常,Monorepo 的根目录包含了胶水代码和业务特定代码,这些代码分享给其他项目也是没有用处的,因此根目录一般不作为一个package发布到 NPM 上,所以我们可以在根目录的package.json里将其标记为private

而在 Monorepo 项目的根目录package.json里也会通过workspaces定义一些 Workspace,每个 Workspace 通过会作为一个package发布到 NPM 上。

wsrun

wsrunopen in new window,用于在 Yarn Workspaces 里运行 NPM 脚本或自定义命令。