node_modules 的目录结构
npm@2 是嵌套结构,npm@3+ 和 yarn 是扁平结构
-
嵌套结构的优点:
- 目录结构比较清晰
-
嵌套结构的问题:
- windows 的最长路径限制
- 相同的依赖没有复用
-
扁平化结构的优点:
- 可以避免依赖包的重复安装,减少磁盘空间的占用。
-
扁平化的问题:
- 幽灵依赖:没有显式声明在 package.json 中的依赖,提升到顶层后,可以被项目直接引用。当有一天这个子依赖不再是引用包的依赖时,项目中的依赖则会出现问题。
- 分身问题:相同依赖的不同版本,只会将其中一个进行提升,剩余的版本还是嵌套在对应的包中。
如下图:npm@3+ 和 yarn 项目可以直接引用 vue 依赖的 postcss,而 pnpm 是不可以的。
pnpm是如何解决上面这些问题
pnpm首先将依赖安装到全局store;- 项目所需的所有的依赖(包括直接依赖和间接依赖)扁平于
node_modules/.pnpm目录下,通过hard link硬链接到全局store; - 将项目的直接依赖通过
symbolic link软连接(符号链接)到node_modules的顶层,
实现了所有项目的依赖共享 store 的全局依赖,解决了幽灵依赖和 NPM 分身的问题
pnpm 的 hardlink 方式
当前测试版本为8.11.0,该版本默认不是硬链接的方式,需要在项目根目录创建 .npmrc 文件,文件内容如下:
package-import-method=hardlink
package-import-method详解: pnpm.io/npmrc#packa…
测试 hardlink 方式是否生效
- 修改后,删除 node_modules;
- 重新安装 vue(
pnpm install vue); - 安装完成后; cd到安装的 vue 目录下(
cd pnpm-test-demo/node_modules/vue); - 获取任意一个文件的 inode(
ls -li index.js);得到 index.js 文件的 inode 为 1515252; - 获取 pnpm 的存储目录 (
pnpm store path); - cd到存储目录下,通过 inode 找到了对应的原文件(
find . -inum 1515252)。
测试直接依赖的软链
pnpm 的克隆
reflink:使用 reflink(也称为写时复制,copy-on-write)导入包。Reflink 是一种文件系统特性,它允许创建一个文件的副本,但只有在修改副本时才会实际复制数据。
调用pnpm实现的relink:
// darwin-arm64-GG6FQZPL.node 根据系统而异
const {reflinkFile, reflinkFileSync} = require('/Users/yanessa/.nvm/versions/node/v20.10.0/lib/node_modules/pnpm/dist/reflink.darwin-arm64-GG6FQZPL.node')
reflinkFileSync( '/Users/yanessa/Documents/code/pnpm-test-demo/test/reflink.js', '/Users/yanessa/Documents/code/pnpm-test-demo/reflink.js')
pnpm 的复制
使用复制(copy)导入包。这意味着 pnpm 会将包的文件复制到项目的 node_modules 目录中。