vite 就是使用的 pmnp 方式 monorepo 仓库,因此要看懂源码,先学学 pnpm 与 monorepo
一、为什么使用 pnpm
- npm 与 yarn 的问题
- 自 npm@3 以来,node_modules 目录扁平化,将依赖的依赖也安装在与其同级的 node_modules 中,这会导致
- 幽灵依赖:项目中可访问 package.json 中不存在的依赖包,当依赖包的依赖更新或删除该依赖包,将导致项目报错
- 依赖扁平化算法复杂
- 相同的依赖包,会被复制到不同的项目中,占用磁盘空间
- npm 为什么采用扁平化目录结构?
- 在 npm@3 之前,采用树形结构,过深的依赖树会造成 Windows 的长目录路径问题
- 当项目中不同的依赖包中有相同的依赖项,会被复制多次
- pnpm 又是如何解决这些问题的呢?
- 幽灵依赖:node_modules 中只存在 package.json 的依赖
- 安装速度快:缓存中有的依赖直接硬链接到 node_modules 中,减少大量 copy IO 操作
- 提高磁盘利用率:通过软硬链接方式,使同版本依赖公用磁盘空间。不同版本只存储额外 diff 文件
- pnpm 原理
- store 集中管理依赖:不同项目相同版本依赖使用硬链接。不同版本依赖只增加 diff 文件
- 项目 node_modules 中只包含 package.json 中依赖 +
.pnpm/,依赖只是软链 - 真实位置在虚拟存储
.pnpm/<name>@<version>/node_modules/<name>中,与 store 中是硬链。由此可见 .pnpm 中平铺存储着所有包,解决了长路径问题,并实现了包隔离 - 依赖的依赖与依赖真实位置同级,并且软链至 ./pnpm 下对应位置
- 常用命令
- 初始化项目:在
<workspace>/subdir执行pnpm init - 安装依赖:
pnpm add或pnpm i - 执行 script:
pnpm runpnpm start为pnpm run start简写
- 执行 node_modulse/.bin 命令:类似与 npx
pnpm exec pnpm create
- 初始化项目:在
- 过滤:将命令限制于特定子级 --filter 或 -F
- 语法
pnpm --filter <package_selector> <command> --filter <package_name>...软件包本身及其所有依赖--filter <package_name>^...不包含本身,只是其所有依赖--filter ...<package_name>本身及依赖于它的包--filter ...^<package_name>依赖于它的包--filter "./packages/**"--filter {packages/\*\*}相对于当前工作目录匹配项目的全局模式。可以与 ... ^ 结合使用pnpm --filter "{packages/**}[origin/master]" <cmd>结合[since]在某个目录选择所有已改项目--filter ...[<since>]指定 commit 或 branch 以来有更改的包--filter=!foo <cmd>除 foo 包外所有--filter xx --filter xx多个 filter 取并集--filter-prod <filtering_pattern>忽略 devDependencies 中依赖pnpm --filter="...[origin/master]" --changed-files-ignore-pattern="**/README.md" run build忽略某个文件造成的变更
- 语法
二、为什么使用 Monorepo 代码管理方式
- Monorepo 演进
- 刀耕火种:是单仓库巨石应用
- 光荣进化:多仓库多模块
- 现在:单仓库多模块
- 有什么优缺点呢
- 代码权限:都在一个仓库没有更细粒度划分,可以看清项目全貌,但增加了修改非 owner 代码风险
- 工程配置:配置容易统一,代码风格相同。依赖只在顶层安装一次,节省磁盘空间
- 开发迭代:方便调试、借助工具自动 link。仓库体积大,clone 时间长
- 构建部署:使用一套 CI CD 流程,借助工具可以配置依赖构建优先级
- 社区有哪些方案
-
Monorepo 构建方案:NX、Turborepo、Rush
-
NX:
- 缓存:本地缓存 与 远程缓存。(提供公共 API,可以本地化远程缓存)
- 增量构建
- 并行构建
- 分布式构建:多台机器同时构建
-
Turborepo
- 缓存:本地与远程。但是没有 API
- 并行构建:开发者控制构建顺序
-
Rush:
- 解决幽灵依赖
- 并行构建
- 插件机制:可扩展性强
- 项目发布:changelog 友好
-
-
版本 & 依赖管理方案
- Lerna
- 作用
- 方便调试、自动 link
- 依赖提升、减少冗余
- 检测 git 代码,自动发版、升级版本号
- 根据 commit 信息,生成更细日志
- 工作模式
- 固定模式:统一升级,版本号一致
- 独立模式:只升级有变动包
- 常用命令
- 初始化:
lerna init - 创建包:
lerna create - 添加依赖,自动 link:
lerna add - 安装依赖:
lerna bootstrap - 清除所有 packages 下依赖:
lerna clean - 发包、并生成 tag:
lerna publish - 执行 scripts 中命令:
lerna run - 列出所有包:
lerna list - 导入已有包:
lerna import
- 初始化:
- 作用
- pnpm + Changeset
- 通过 pnpm-workspace.yaml 文件定义工作空间
workspace:协议,安装依赖时智能从 workspace 安装。不存在则报错
- Lerna
packages:
# all packages in direct subdirs of packages/
- 'packages/*'
# all packages in subdirs of components/
- 'components/**'
# exclude packages that are inside test directories
- '!**/test/**'