Node.js 16 中加入了 corepack 工具,能够直接启用 pnpm 与 yarn 作为包管理器,而不需要通过 npm 进行额外的安装。
尝试了在日常开发中使用 pnpm 与 yarn 替代 npm,记录了一些体验上的优化和实际使用中遇到的问题。
通过 corepack 启用 pnpm 与 yarn
corepack 目前还是实验性功能,需要按照文档1进行手动开启。
- 执行
corepack enable
(如果 Node.js 未以 user 权限进行安装,需要 sudo 或 root 权限进行运行,windows 上需要以管理员模式运行) - 配置
package.json
中的packageManager
(使用 pnpm 时即使不配置也可正常使用,使用 yarn 时如果不进行packageManager
的配置,会使用 yarn 1 而非 yarn 2+) - 如果需要在离线环境下启用,可以使用
corepack prepare
进行预载
pnpm 与 yarn 的优势
Phantom dependencies
Phantom dependencies 是指依赖被安装后,非该依赖链上的代码也可以使用该依赖,例如
// root
{
"dependencies": {
"a": "^1.0.0",
"c": "^1.0.0"
}
}
// a
{
"dependencies": {
"b": "^1.0.0"
}
}
安装后文件路径为
/node_modules
/a
/b
/c
其中 b 依赖可以被直接import
/require
,这造成了隐式依赖,可能出现不易发现的问题,详细说明可以参见 rushjs 的解释2。
NPM doppelgangers
NPM doppelgangers 是指同一依赖存在不同版本时,尽管 npm 进行了 hoist,仍会产生多个重复的依赖包,例如
{
"dependencies": {
"a": "^1.0.0", => { "e": "^1.0.0" }
"b": "^1.0.0", => { "e": "^1.0.0" }
"c": "^1.0.0", => { "e": "^2.0.0" }
"d": "^1.0.0" => { "e": "^2.0.0" }
}
}
安装后文件路径为
// npm 进行 hoist
/node_modules
/a
/b
/c
/e@2
/d
/e@2
/e@1
// 或
/node_modules
/a
/e@1
/b
/e@1
/c
/d
/e@2
这样会导致重复依赖的存在不可避免,影响性能甚至会导致编译工具的一些 bug,详细说明可以参见 rushjs 的解释3。
pnpm 与 yarn 的优化
pnpm 采用 symlink 方式4,在项目中通过建立共享存储的 hardlink 来创建非平铺(non-flat)的 node_modules,从而避免了上述的问题,同时也减少了硬盘空间的占用。
yarn (2+) 采用 Plug’n’Play 方式,通过一个 cjs 控制依赖的加载,以 zip 方式提供依赖,从而能够约束 phantom dependencies 问题,同时减少了硬盘的 IO。
并且在这种方式下,将依赖 commit 进 git 成为可能,配合 yarn 的 zero install 功能,能够使项目更加稳健,不因依赖安装产生问题。
使用中的一些问题
yarn
在遇到一些不很规范的包时,yarn PnP 会因为包调用 phantom dependencies 而出现依赖缺失,需要使用 loose 模式5绕过。
同时,yarn PnP 会以 zip 格式安装依赖,需要额外安装 sdk 与相关插件等使编辑器能够正常工作,在一些受限环境下使用不友好。
pnpm
使用 hardlink 创建 non-flat 的 node_modules 有时候会导致文件读取问题。
一个例子为,曾经遇到过在 Windows 10 环境下,通过 vite 运行依赖 @mui/icons-material 的应用时,出现 too many files 问题。
临时的解决方案为使用
// ok
import { xxx } from "@mui/icons-material/XXX"
替代
// error: too many files
import { xxx } from "@mui/icons-material"
但这个问题在 Linux 下没能复现,不能确定是什么原因。并且由于手边没有能复现的环境,暂时也不能排查问题。
总结
Node.js 增加 corepack 后,相信 yarn 与 pnpm 会被更为广泛地应用,从而解决一些 npm 一直存在的问题。
从个人使用角度,yarn 与 pnpm 也确实提升了体验与开发效率,值得作为 Node.js 下主要的包管理工具。