首页 > 其他分享 >[pnpm] pnpm 与 npm/yarn 的对比

[pnpm] pnpm 与 npm/yarn 的对比

时间:2024-08-14 19:05:39浏览次数:12  
标签:npm node 依赖 NPM modules yarn pnpm 链接

JavaScript 应用程序通常依赖于许多外部库,这些依赖项通常通过包管理器来管理。默认情况下,Node.js 使用 NPM 作为包管理器。

由于早期的 NPM 存在各种不足,社区后来开发了 Yarn 和 pnpm 作为替代品。

如果要使用 Yarn 和 pnpm,则需要先通过 NPM 进行安装。

早期 NPM 的不足

image-20240814145147621

  1. 依赖树过深

    在 NPM 3.0 之前,NPM 使用了嵌套依赖树的结构。这意味着如果一个项目的多个依赖项需要同一个包的不同版本,NPM 会在每个依赖项的目录中重复安装该包。这种结构会导致 node_modules 目录非常深,特别是在 Windows 系统中,这可能导致路径长度限制的问题。

  2. 重复安装和磁盘空间浪费

    每次安装包时都会重新从头开始解决依赖关系,并逐个下载和安装包。即使是已经安装过的包,也可能会再次下载,而没有利用缓存机制。这种重复安装的策略会导致安装十分缓慢。

  3. 依赖版本不确定

    在早期版本的 NPM 中,没有类似 yarn.lockpackage-lock.json 这样的锁文件。这意味着即使 package.json 中指定了版本范围(例如^1.0.0这种表示可以接受一个范围的版本),依赖关系的解析和安装仍然是动态的,可能会因为时间或网络状态的不同而导致不同的版本被安装。

  4. 扁平化依赖树

    NPM 在 3.0 版本引入了扁平化依赖树,以解决早期版本中嵌套依赖树带来的问题,但是扁平化依赖带来了新的问题。

    • 依赖冲突:扁平化依赖树的设计将所有依赖项都安装在项目的根 node_modules 目录中,这意味着多个包可能会共享同一个依赖项的版本。如果不同的包需要不同版本的相同依赖项,就可能会发生冲突。
    • 幽灵依赖:依赖的依赖被平铺在根node_modules目录中,这意味着即使应用的package.json中没有声明的依赖,也可以被引入并使用。这种现象会导致依赖关系和依赖版本的不明确。

    image-20240814151136457

    图中的虚线就代表幽灵依赖,也叫隐式依赖

    依赖 E 原本是 B 的依赖,但是被扁平化后提升到 node_modules 顶层。

    这个 E 没有被显式地在 package.json 中声明,但是结合 node.js 的模块解析机制可知这个依赖是可以被 Project 引入的。

    这种 意料之外 的依赖关系会使得项目难以维护。

Yarn

Yarn的提出是为了解决 NPM 的不足,它具有以下特点:

  1. 确定性安装

    Yarn 引入了 yarn.lock锁文件,明确了依赖的版本。

  2. 更快更小

    Yarn 通过并行下载以及引入缓存机制来加快安装速度,并且由于缓存的存在,在离线状态下也可以安装已缓存过的依赖。

  3. 扁平化依赖结构

    减少了路径深度,提高了依赖解析的速度。解决了依赖冲突问题:Yarn 会通过将不同版本的依赖项放在各自子目录的 node_modules 中来解决冲突,而不是强制将所有依赖都安装在顶层。

    image-20240814151859723

  4. 可以通过配置 workspaces 支持 monorepo。

不足

  • 没有解决幽灵依赖的问题;
  • workspaces 配置较繁琐。

pnpm

image-20240814142111601

pnpm的特点:

  1. 节省磁盘空间

    npm 和 Yarn 会在每个项目的 node_modules 目录中为所有依赖项存储完整的文件副本。如果有多个项目依赖相同的包,那么这些包会被重复存储

    pnpm 使用中心化的 store 统一存储安装的包,项目内的依赖通过链接指向 store 中的依赖。如果有多个项目依赖相同的包,都指向 store 中单一的包。

  2. 安装速度更快

    pnpm npm/yarn/...
    1. 依赖解析。 仓库中没有的依赖都被识别并获取到仓库。
    2. 目录结构计算。 node_modules 目录结构是根据依赖计算出来的。
    3. 链接依赖项。 所有以前安装过的依赖项都会直接从仓库中获取并链接到 node_modules
    1. 解析所有依赖。
    2. 获取所有依赖。
    3. 将所有依赖写入node_modules
    pnpm 安装过程的插图。 所有的包会尽快解吸、获取和硬链接。 一个 Yarn Classic 或 npm 这样的包管理器如何安装依赖项的示意图。

    pnpm 的中心化store可以更大程度地复用依赖包,使得安装依赖这一步骤更快完成。

  3. 支持 monorepo,配置比起 yarn 来说相对简单,并且得益于 pnpm 的特性,安装依赖很快。

  4. 非扁平化的 node_modules

    上文说到 yarn 和 npm 为了解决路径过长、依赖管理复杂等问题,将依赖进行扁平化管理。但是也带来了幽灵依赖等新问题。

    pnpm 的创新点在于提出了 基于符号链接的非扁平化 node_modules 结构,解决了幽灵依赖问题。

    硬链接和软链接

    在 Linux 操作系统中,每一个文件对应一个 inode(索引节点)。链接是一种在共享文件和访问它的用户的若干目录项之间建立联系的一种方法。

    image-20240814164425085
    • 硬链接是文件的别名,和源文件指向同一个 inode。即硬链接和源文件是同一个文件
    • 软连接也叫符号链接,是一种特殊的文件类型,其中包含对另一个文件的引用。软链接可以看作是对一个文件的间接指针,类似于 Windows 操作系统下的 快捷方式即软链接和源文件是不同文件

    在 Windows 中也有软硬链接的概念,在 cmd 中通过 mklink指令创建链接:

    • 硬链接:

      mklink /H link_name target_file
      
    • 软链接

      mklink link_name target_file
      

    pnpm的node_modules结构

    文件结构示例:comparing-node-modules/pnpm5-example at master · zkochan/comparing-node-modules (github.com)

    pnpm将实际的依赖文件都安装到全局store中,在项目中的node_modules文件夹内通过创建链接来使用store中的依赖。

    与 yarn 和 npm 直接将所有依赖平铺在 node_modules 中的做法不同,pnpm 在 node_modules 中创建了一个 .pnpm文件夹,再将所有依赖都平铺在这个文件夹中。这样 node.js 的模块解析算法就无法引入非顶层依赖了,故解决了幽灵依赖问题。

    .pnpm 中的依赖通过软链接建立依赖之间的父子关系,并通过硬链接指向实际存在于全局store中的依赖包。

    在 package.json 中显式声明的依赖会通过软链接提升到 node_modules 文件夹下,因此 node.js 可以正常解析 package.json 中声明的依赖。

    .pnpm 中,依赖通过.pnpm/<name>@<version>/node_modules/<name>的形式进行记录,可以看到同一个包的不同版本会被分开记录

    image-20240814181732826

    如上图,项目中只有 express 这一个依赖,而 express 有许多子依赖,这里只列举了 qs 这一个依赖。

    可以观察到,这种基于链接的 node_modules 结构实现了:

    1. 项目的 node_modules 只能解析到 package.json 中显式声明的依赖,解决了幽灵依赖问题
    2. 所有依赖都被平铺在 .pnpm 文件夹中,不会导致过长的文件路径
    3. 实际的依赖被安装在全局的store中,项目中仅通过硬链接进行关联,节省了磁盘空间
    4. 观察到express和它的依赖同属于一个文件夹层级(图中蓝色区域),express 所有的依赖都软链至了 node_modules/.pnpm/ 中的对应目录。 把 express 的依赖放置在同一级别避免了循环的软链。

现在的 NPM

yarn 和 pnpm 属于社区产物,NPM 作为官方的包管理器,一直在吸收社区好物的优点。

现在的 NPM 也有了锁文件来明确依赖的版本,并且也通过使用缓存、改进依赖解析算法等手段加速了安装。

NPM 在 7.0 版本之后也支持配置 monorepo 了,可以在 package.json 中直接配置,但是只支持一些简单的功能。yarn 则提供了插件系统。

总结

特点 NPM Yarn pnpm
安装速度 较慢 较快 大部分情况下比 Yarn 块
依赖管理 直接安装到 node_modules 通过缓存加速安装 中心化 store,依赖通过符号链接安装
磁盘空间使用 中等 最低,通过去重和链接机制
依赖冲突处理 容易出现冲突 通过锁文件和解析依赖减少冲突 严格隔离各依赖版本,减少冲突
锁文件 package-lock.json yarn.lock pnpm-lock.yaml
幽灵依赖问题 可能发生 可能发生 严格依赖树,避免幽灵依赖
monorepo支持 基础支持 功能丰富,包含插件系统 高效的工作空间管理,模块共享更优化
安装一致性 可能由于缓存和平台差异而不一致 高,一致性较好 更高,通过全局硬链接机制确保一致性

性能对比图像来自 pnpm 官方文档:Benchmarks of JavaScript Package Managers | pnpm中文文档 | pnpm中文网

Graph of the alotta-files results

标签:npm,node,依赖,NPM,modules,yarn,pnpm,链接
From: https://www.cnblogs.com/feixianxing/p/18359597/node-package-manager-compare-pnpm-vs-npm-an

相关文章

  • [pnpm] pnpm 与 npm/yarn 的对比
    JavaScript应用程序通常依赖于许多外部库,这些依赖项通常通过包管理器来管理。默认情况下,Node.js使用NPM作为包管理器。由于早期的NPM存在各种不足,社区后来开发了Yarn和pnpm作为替代品。如果要使用Yarn和pnpm,则需要先通过NPM进行安装。早期NPM的不足依赖树......
  • 【Spark集群部署系列四】Spark on YARN介绍和环境部署
    简介引言:通常如果我们想要一个稳定的生产Spark环境,那么最优的选择就是构建:HAStandAlone集群.。不过在企业中,服务器的资源总是紧张的,许多企业不管做什么业务,都基本上会有Hadoop集群.也就是会有YARN集群。对于企业来说,在已有YARN集群的前提下在单独准备Spar......
  • yarn + vue3 + vite 创建项目
    yarncreatevite//通过yarn+vite创建项目填写的创建的项目名称,默认名称vite-project 选择框架,我们点击上下键,回车选择,我们这里选择vue 选择语言,我们点击上下键,回车选择,我们这里选择TypeScript,即ts,然后回车 创建成功,如下 按照提示,执行命令cdvite-project......
  • NPM依赖管理:精通版本范围锁定策略
    引言在JavaScript项目开发中,依赖包的精确控制对于维护项目稳定性至关重要。NPM(NodePackageManager)作为Node.js的包管理器,提供了一套灵活的版本控制机制,允许开发者通过版本范围锁定策略来管理依赖包的更新。本文将详细介绍如何使用NPM设置包的版本范围锁定策略,帮助开发者......
  • NPM依赖管理:掌握自动更新策略的艺术
    引言在现代JavaScript应用开发中,依赖包的自动更新是保持项目现代化和安全性的关键。NPM(NodePackageManager)作为Node.js生态系统的心脏,提供了一套强大的版本控制机制,允许开发者设置自动更新策略。本文将详细介绍如何使用NPM设置包的版本自动更新策略,帮助开发者自动化依赖......
  • npm报错:request to https://registry.npm.taobao.org failed处理办法
    今天在安装flowise的时候提示npm报错:requestto https://registry.npm.taobao.org failed,reasoncertificatehasexpired看提示是淘宝镜像过期了。找了一下资料,好像是npm淘宝镜像已经从 registry.npm.taobao.org 切换到了 registry.npmmirror.com。旧域名也将于2022......
  • node npm nvm的恩怨情仇
    nvmnvminstall18.19.1nvmuse18.19.1node-vQ程序“node.exe”无法运行:指定的可执行文件不是此操作系统平台的有效应用程序。https://www.cnblogs.com/rnny/p/17839190.html注意比对nvm文件夹内的setting.txt文件内容rootnvm文件夹(path中的NVM_HOME)pathn......
  • 掌握NPM版本控制:项目维护与依赖管理的艺术
    在软件开发过程中,版本控制是确保项目稳定发展的关键。NPM(NodePackageManager)作为Node.js的包管理工具,不仅提供了包的安装和管理功能,还内置了一套版本控制机制,帮助开发者管理项目的不同版本。本文将详细介绍如何使用NPM进行项目的版本控制。版本控制的重要性在多人协作的......
  • 深入探索NPM:常用命令及其应用场景解析
    NPM(NodePackageManager)是JavaScript编程语言的包管理器,它允许开发者安装和管理有依赖的包,以及发布自己的包。作为Node.js生态系统中的核心工具,NPM提供了一系列的命令,用于项目的依赖管理、版本控制、包发布等。以下是一些NPM的常用命令及其作用的详细介绍。1.npminit此......
  • npm 常用命令
    1.npmrunbuild>build.log2>&1npmrunbuild>build.log2>&1在命令行中,>build.log2>&1是一个重定向操作,用于将程序的标准输出和标准错误输出重定向到一个文件中。这对于记录日志和调试非常有用。以下是详细的解释:命令解释>:用于重定向标准输出(stdout)。默认情......