monorepo 是什么
monorepo 是把多个项目的所有代码放到一个 git 仓库中进行管理,多个项目中会有共享的代码则可以分包引用。整个项目就是有 root 管理的 dependencies 加上多个 packages,每个 package 也可以在自己的作用域引入自己的 dependencies。
项目结构如下:
1 | ├── node_modules |
packages 文件夹中的就是原本每个独立的项目(下文称之为 package )了,现在放在一起用 workspace 去管理。最外层路径称之为 root。在 root package.json 中的 deps 是所有子 package 共用的。
pnpm 是什么
Fast, disk space efficient package manager
pnpm 是新一代 node 包管理器。它由 npm/yarn 衍生而来,但却解决了 npm/yarn 内部潜在的 bug,并且极大了地优化了性能,扩展了使用场景。[^1]
pnpm 相比 yarn,npm,yarn PnP 安装包更快速,对包的依赖管理更偏平,对磁盘占用也有优势, 类似maven中央仓库,统一依赖目录。
具体可以参考这篇文章:为什么现在我更推荐 pnpm 而不是 npm/yarn?
为什么要使用 monorepo
使用 monorepo 可以把原本一个项目的多个模块拆分成多个 packages,在 packages 之间相互引用,也可以单独发布成包,极大地解决了项目之间代码无法重用的痛点。在项目打包或者编译操作时也可重用一套配置,通吃所有 packages。
开始
首先需要安装pnpm, 然后npm初始化一个项目。
1 | curl -fsSL https://get.pnpm.io/install.sh | sh - |
在 root 目录新建 pnpm-workspace.yaml,内容如下
1 | packages: |
我们所有的 packages 都放在 packages 目录下。
用 pnpm 安装全局共用的包,比如 react, react-dom。
1 | pnpm install react react-dom -w |
注意这里使用 -w 表示把包安装在 root 下,该包会放置在
1 | pnpm i dayjs -r --filter @test/web |
使用 –filter 后面接子 package 的 name 表示只把安装的新包装入这个 package 中。
接下来,我们在 packages 中新建以下几个目录。
1 | ├── packages |
然后每个都执行 npm init ,假设每个 package 的 name 依次为 @test/ui @test/utils @test/web。
1 | // packages/utils |
以 utils 为例,入口文件为 index.ts,首先建立这个文件。写入如下内容。
1 | export const add = (a: number, b: number) => a + b |
然后,执行
1 | pnpm i @test/utils -r --filter @test/ui |
之后,打开 packages/ui/package.json 发现 dependencies 中多了一行。
1 | { |
由于是 workspace 管理的,所有有一个前缀 workspace。接下来则可以从 package/ui 中直接引入这个包了。
1 | import {add} from '@test/utils' |
那么接下来的 package/web 就是整个项目的整体了。放置原来项目中的所有 src 下的代码。而一些原本通用的代码就从 src 下提取成包放在了 packages 下了。这样就好理解了。