pnpm 介绍
目前前端包管理的根基是 npm,在其基础上衍生出了 yarn、pnpm。在 2022 年以后,我们推荐使用 pnpm 来管理项目依赖。pnpm 覆盖了 npm、yarn 的大部分能力,且多个维度的体验都有大幅度提升。
具有以下优势
- 速度快:多数场景下,安装速度是 npm/yarn 的 2 - 3 倍。
- 基于内容寻址:硬链接节约磁盘空间,不会重复安装同一个包,对于同一个包的不同版本采取增量写入新文件的策略。
- 依赖访问安全性强:优化了 node_modules 的扁平结构,提供了限制依赖的非法访问(幽灵依赖)的手段。
- 支持 monorepo:自身能力就对 monorepo 工程模式提供了有力的支持。在轻量场景下,无需集成 lerna、Turborepo 等工具。
pnpm 支持 monorepo 模式的工作机制叫做 workspace(工作空间)。它要求在代码仓的根目录下存有 pnpm-workspace.yaml 文件指定哪些目录作为独立的工作空间,这个工作空间可以理解为一个子模块或者 npm 包。
需要注意的是,pnpm 并不是通过目录名称,而是通过目录下 package.json 文件的 name 字段来识别仓库内的包与模块的。
包管理
中枢管理操作
创建一个 package.json 文件
pnpm init
设置用户的全局
.npmrc配置pnpm config set <key> <value>- 也可以在根目录直接创建
.npmrc文件加上一行设置镜像源:registry=https://registry.npm.taobao.org
根据当前目录
package.json中的依赖声明安装全部依赖,在 workspace 模式下会一并处理所有子模块的依赖安装。pnpm install
根目录的创建
pnpm-workspace.yaml文件指定包目录- packages/*
添加
.gitignore文件排除 node_modulesnode_modules
安装项目公共开发依赖,声明在根目录的
package.json - devDependencies中。-w 选项代表在 monorepo 模式下的根目录进行操作。每个子包都能访问根目录的依赖,适合把 TypeScript、Vite、eslint 等公共开发依赖装在这里。
pnpm install -wD xxx
卸载公共依赖,在根目录的
package.json - devDependencies中删去对应声明pnpm uninstall -w xxx
执行根目录的 package.json 中的脚本
pnpm run xxx
子包管理操作
- 为指定模块安装外部依赖。
- 下面的例子指为 a 包安装 lodash 外部依赖。-S 和 -D 选项分别可以将依赖安装为正式依赖(dependencies)或者开发依赖(devDependencies)。
pnpm --filter a i -S lodash
pnpm --filter a i -D lodash
- 下面的例子指为 a 包安装 lodash 外部依赖。-S 和 -D 选项分别可以将依赖安装为正式依赖(dependencies)或者开发依赖(devDependencies)。
- 指定内部模块之间的互相依赖。
- 指定模块之间的互相依赖。下面的例子演示了为 a 包安装内部依赖 b。
pnpm --filter a i -S b
- 指定模块之间的互相依赖。下面的例子演示了为 a 包安装内部依赖 b。
- 过滤的高级用法
- 用 –filter 过滤出目标工作空间集合后,不仅支持 install 安装依赖,run(执行脚本)、publish(发布包) 等绝大多数包管理操作都能够执行。
发布所有包名为 @a/ 开头的包
pnpm --filter @a/\* publish - –filter 的还有更多超乎我们想象的能力,它支持依赖关系筛选,甚至支持根据 git 提交记录进行筛选。
为 a 以及 a 的所有依赖项执行测试脚本
pnpm –filter a… run test
为 b 以及依赖 b 的所有包执行测试脚本
pnpm –filter …b run test
找出自 origin/master 提交以来所有变更涉及的包
为这些包以及依赖它们的所有包执行构建脚本
README.md 的变更不会触发此机制
pnpm –filter=”…{packages/}[origin/master]”
–changed-files-ignore-pattern=”/README.
md” run build
找出自上次 commit 以来所有变更涉及的包
pnpm –filter “…[HEAD~1]” run build
开始
- 创建工程
1 | mkdir openx-ui |
- 根目录生成
package.json文件,去掉 name 以外的所有字段
1 | { |
- 根目录下创建 pnpm-workspace.yaml 文件
1 | packages: |
- 设置根目录 package.json
1 | { |
private: true:根目录在 monorepo 模式下只是一个管理中枢,它不会被发布为 npm 包。
devDependencies:所有模块都会有一些公共的开发依赖,例如构建工具、TypeScript、Vue、代码规范等,将公共开发依赖安装在根目录可以大幅减少子模块的依赖声明。
- 组件包的 package.json
1 | { |
name:组件统一发布到 @openxui 坐标下,有坐标限制了命名空间,组件的名称可以尽可能简单。
files:我们规定每个包的产物目录为 dist,此外还要一并发布 README.md 文档。
publishConfig:如果我们需要发布到私有 npm 仓,请取消 publishConfig 的注释并根据实际情况填写。
peerDependencies: 既然是使用 vue3 的组件库,我们需要正确声明主框架的版本。这里不将 vue 放入 dependencies 是因为用户项目同样也直接依赖 vue 框架,这样可能造成依赖版本不同的风险。这就是为什么周边库、插件总是要把主框架声明为 peerDependencies 的原因,我们的组件库也不例外。
dependencies:项目的运行依赖都安装在这里。一般不容易或是不介意出现版本冲突的依赖都放在这里。比如 lodash 这样的工具方法库,即使版本冲突出现多实例的现象,也不会出现问题。
devDependencies:大部分开发依赖都会被定义在根目录下,这里只会声明特有的、本模块专属的开发依赖。比如某个特定的 Vite 插件。
- 项目文档的 package.json
1 | { |
private: true:项目文档的 packages.json 与根目录类似,它同样不需要被发布到 npm 仓库。
dependencies和devDependencies:由于不涉及发包,因此依赖声明无需仔细考量,安装到那个里面效果都是一样的。不过还是建议大家还是按照“实际的含义”来决定安装类型。
- 最后根目录加上
.gitignore
1 | # .gitignore |