转移至pnpm提案

338 阅读3分钟

方案背景

问题1

在升级 swr 的过程中发现了主应用对“幽灵依赖”的使用,这是主应用的代码,引用了 swr 但是我们并没有在 pkg 对其进行声明

image.png

竟然主应用 pkg文件里并没有对 swr 进行声明,swr 从何而来?幽灵依赖产生于 npm 扁平化结构;以下是我安装了@nexus/scripts的 node_module 文件列表,这意味着我明明在 pkg 文件里声明了一个@nexus/scripts却可以在主应用中使用到swr、axios、react等,这些就是幽灵依赖

image.png

我觉得这是一个潜在的bug,比如你引用了一个devDependence的子依赖,在本地开发可能没有任何问题,但是到了线上问题就出现了,线上环境不会安装devDependence,也就不会有它的子依赖。

问题2

npm 下相同的包重复安装,也就是“依赖分身”问题,比如我的主用声明了一个swr@1.0.0@nexus/hooks@1.0.0,其中@nexus/hooks@1.0.0引用了swr@1.0.0

image.png

用npm 安装 node_module 是这样的:

WeChatWorkScreenshot_e81ff928-4dfb-4e1b-a6a3-82bc14153c9e.png

有两个swr@1.0.0?合理来说我只需要一个swr@1.0.0 在根的 node_module 就行了不是吗?

优势分析

不再有幽灵依赖

pnpm 创建的是一个非扁平化的node_module 项目结构我安装了 @nexus/hooks 那么我的根下也就只有它,没有其他乱七八糟的东西,清晰明了

WeChatWorkScreenshot_1b4d4698-2bc2-4df0-879d-b1dad64c1842.png

通过软链的形式实现对分身的复用

image.png

咋一看好像还是两个包

image.png

实际上hooks下的这个包是一个软链

速度优势

当使用 npm 或 Yarn 时,如果你有 100 个项目,并且所有项目都有一个相同的依赖包,那么, 你在硬盘上就需要保存 100 份该相同依赖包的副本。然而,如果是使用 pnpm,依赖包将被存放在一个统一的位置,因此:

  1. 如果你对同一依赖包需要使用不同的版本,则仅有版本之间不同的文件会被存储起来。例如,如果某个依赖包包含 100 个文件,其发布了一个新版本,并且新版本中只有一个文件有修改,则 pnpm update 只需要添加一个新文件到存储中,而不会因为一个文件的修改而保存依赖包的所有文件。
  2. 所有文件都保存在硬盘上的统一的位置。当安装软件包时,其包含的所有文件都会硬链接自此位置,而不会占用额外的硬盘空间。这让你可以在项目之间方便地共享相同版本的依赖包。

最终结果就是以项目和依赖包的比例来看,你节省了大量的硬盘空间, 并且安装速度也大大提高了!

具体实践

首先有一点我要说明,大胆实践,基本没有额外的学习成本,pnpm 的使用方式与 npm 的使用方式基本相同,低成本好东西为什么不用?

安装 pnpm

  • npm i pnpm -g

把过去使用的“幽灵依赖”进行显示声明

  • pnpm install后启动项目根据报错对“幽灵依赖”进行显示声明: image.png

执行命令

  • pnpm import 根据 yarn.lock 或者 package.lock 生成 pnpm.lock
  • pnpm install 开始安装依赖

其他问题

基本根据上述的步骤不会出现问题;如果还有问题,可以配置.npmrc 将这些包提升至根node_module就可以了

比如我们项目中一些包引用了 react16,而根是 react17,出现这样的报错 WeChatWorkScreenshot_740af223-cbe4-4569-8dcd-57535fbc623f.png

.npmrc 配置

# root/.npmrc
# 把相关库提升到公共(项目)node_modules下
public-hoist-pattern[]=*ahooks*
public-hoist-pattern[]=*@ant-design*

# 使用扁平方式,除非实在定位不到问题,不建议使用,这样就又回到 npm、yarn 的使用了
# shamefully-hoist=true