当前位置: 首页 > news >正文

npm, yarn, pnpm之间的区别

在这里插入图片描述

前言

在现代化的开发中,一个人可能同时开发多个项目,安装的项目越来越多,所随之安装的依赖包也越来越臃肿,而且有时候所安装的速度也很慢,甚至会安装失败。

因此我们就需要去了解一下,我们的包管理器,在前端比较主流的包管理器主要有三个(当然还有其他优秀的包管理器,本文主要介绍这三个),分别是:npm,yarn,pnpm

幽灵嵌套(Phantom Dependency)

在了解包管理器之前,我们先了解一下包管理的一个难题:幽灵嵌套

幽灵嵌套问题通常发生在依赖之间存在复杂的版本要求时,比如:

  • 包 A 依赖于包 B@1.0.0
  • 包 B 依赖于包 C@2.0.0
  • 另一个包 D 也依赖于 C@3.0.0

在传统的依赖管理中,可能会导致包 C 的不同版本被嵌套在不同的子依赖树中,从而在 node_modules 中形成不同路径的多层嵌套,导致路径非常深。这种情况一旦发生,如果包 A 和包 D 不同意版本要求,可能会导致不同版本的包 C 被分别安装在不同的路径下,出现路径冲突甚至依赖问题,这就是“幽灵嵌套”现象。

NPM (Node Package Manager)

概述:

npm 是 Node.js 默认的包管理工具,最早由 Node 社区开发并捆绑到 Node.js 中,因此使用最为广泛。

从npm v2 到npm v7+的升级过程中,从最初使用递归的方法处理依赖,造成高度嵌套的依赖树,到后来使用扁平化管理,一定程度上解决了依赖嵌套问题,但是出现了算法时间过长的问题,最后引入了package-lock.json机制,作用是锁定依赖结构,一定程度上保持了依赖的稳定性

核心:

  • 采用扁平化依赖管理管理方式
  • 每个依赖包都会在 node_modules 中单独安装
  • 相同的依赖可能会被重复安装多次

特点:

  • 优点:
  1. Node.js 默认包管理器,使用最广泛,拥有强大的额社区支持
  2. 最早的包管理器,简单易上手,对初学者友好
  3. package-lock.json 保证依赖版本一致性
  • 缺点:
  1. 安装速度较慢,占用空间大

目录结构:

  • npm v2及以前
  1. 依赖树可能非常深
  2. 相同包会重复安装
  3. 占用大量磁盘空间
  4. 文件路径可能超过 Windows 限制
node_modules
├── A
│   └── node_modules
│       └── B
│           └── node_modules
│               └── C
└── D└── node_modules└── B└── node_modules└── C
  • npm v3-6,扁平化管理
  1. 采用扁平化优先的安装策略
  2. 相同版本的包会被提升到顶层
  3. 不同版本保留在各自的 node_modules 中
  4. 安装算法比较复杂,需要计算依赖树
// 假设依赖关系:
// package-A 依赖 lodash@4.0.0
// package-B 依赖 lodash@4.0.0node_modules
├── package-A
├── package-B
└── lodash     // 被提升到顶层

当有版本冲突时:

// package-A 依赖 lodash@4.0.0
// package-B 依赖 lodash@3.0.0node_modules
├── package-A
├── package-B
│   └── node_modules
│       └── lodash  // 3.0.0 版本
└── lodash         // 4.0.0 版本提升到顶层
  • npm v7+, 改进了扁平化管理,引入peer dependencies 处理
  1. 自动安装 peer dependencies
  2. 更严格的版本锁定
  3. 改进了依赖解析算法
  4. workspaces 支持
node_modules
├── package-A
├── package-B
├── lodash        // 主版本
└── .package-lock.json  // 更严格的版本锁定

Yarn

概述:

Yarn 是一个 JavaScript 包管理工具,最早由 Facebook 推出,主要用于管理项目中的依赖包。和 npm 类似,yarn 解决了在 JavaScript 项目中下载、安装和管理依赖的需求,并在一定程度上改进了 npm 的一些缺点,比如性能、稳定性和安全性。

核心:

  1. 并行下载提升安装速度:

传统的 npm 安装方式是依次下载依赖,而 Yarn 可以同时下载多个依赖,称为“并行下载”。这种方式充分利用了网络带宽,显著减少安装依赖所需的时间,使得安装速度更快。并行下载尤其在大型项目中效果显著,能够有效降低整体安装时间。

  1. 缓存机制减少重复下载:

Yarn 内置了缓存机制,在首次安装依赖时会将其缓存到本地。之后再次安装这些依赖时,如果依赖版本没有改变,Yarn 会直接从本地缓存中读取,而不是重新下载。这样不仅节省了网络请求,还提升了安装速度,特别适合离线开发和持续集成场景。

  1. yarn.lock 确保依赖版本一致性:

Yarn 使用 yarn.lock 文件记录每个依赖的具体版本和来源,确保团队所有成员在不同机器上安装时得到的依赖版本完全一致。这避免了“依赖地狱”问题,即由于版本不一致导致的错误或不兼容情况,从而提高了开发过程的稳定性。

  1. 更安全的依赖解析机制:

Yarn 在安装依赖时会校验每个包的完整性(如 SHA 校验),以确保包的内容没有被篡改。这种安全机制能在下载依赖时检测到潜在的包篡改或恶意代码的引入,增强了项目的安全性。相比于早期的 npm,Yarn 的这种依赖解析机制更加严谨。

  1. Workspace 支持更好的 monorepo 管理:

Yarn 支持 Workspace 功能,允许在单个代码库(monorepo)中管理多个项目或包。这种管理方式可以将多个子项目的依赖集中管理、共享,减少重复依赖的安装。此外,Yarn 还能通过 Workspace 在多个包之间建立相互依赖关系,使 monorepo 项目的开发、构建和测试更加高效。

  1. PnP(Plug’n’Play)模式提供更快的模块加载:

Yarn 2.x 引入了 PnP 模式,这种模式完全去除了 node_modules 目录,通过在 .pnp.cjs 文件中记录依赖映射关系。PnP 不仅减少了磁盘空间的占用,还提升了依赖的加载速度,因为 Node.js 不再需要递归遍历 node_modules。这样可以加快应用的启动速度,同时在依赖数量庞大的项目中减少文件系统的压力。

特点:

  • 优点:
  1. 采用扁平化优先 + 符号链接(符号链接是一个特殊的文件,它包含对另一个文件或目录的引用路径)的组合策略
  2. 相同版本的包会被提升并复用
  3. 不同版本通过符号链接保持正确的引用关系
  • 缺点:
  1. 仍然存在幽灵依赖问题
    尽管 Yarn 已经在扁平化和依赖管理上做了优化,但在一些复杂的项目中仍然会出现幽灵依赖问题。所谓幽灵依赖,指的是某个包在项目中使用但并未在 package.json 中声明,可能是通过其他依赖的间接依赖引入。这种隐式依赖会导致项目依赖关系难以维护,如果间接依赖被移除,可能会导致项目出错。

  2. 某些场景下的依赖解析较慢
    Yarn 的依赖解析虽然比传统 npm 更快,但在依赖结构复杂、依赖版本冲突较多的情况下,解析和处理依赖关系可能会变慢。尤其在 monorepo 中,Yarn 需要处理多个包之间的依赖关系,可能出现解析速度不如 pnpm 的情况。

目录结构:

  • 基本结构
node_modules/
├── package-A/                # 实际文件
├── package-B/                # 实际文件
├── lodash/                   # 提升到顶层的共享包
└── .bin/                     # 可执行文件的符号链接
  • 依赖共享案例
// 假设有以下依赖关系:
项目
├── package-A (依赖 lodash@4.0.0)
└── package-B (依赖 lodash@4.0.0)// Yarn 会创建这样的结构:
node_modules/
├── package-A/
│   └── node_modules/
│       └── lodash -> ../../../lodash  # 符号链接
├── package-B/
│   └── node_modules/
│       └── lodash -> ../../../lodash  # 符号链接
└── lodash/                            # 实际文件
  • 版本冲突处理
// 当存在版本冲突时:
项目
├── package-A (依赖 lodash@4.0.0)
└── package-B (依赖 lodash@3.0.0)// Yarn 会这样处理:
node_modules/
├── package-A/
│   └── node_modules/
│       └── lodash -> ../../../lodash    # 指向4.0.0
├── package-B/
│   └── node_modules/
│       └── lodash/                      # 本地安装3.0.0
└── lodash/                              # 4.0.0版本在顶层

PNPM(Performant NPM)

概述:

pnpm 是一个更现代化的包管理工具,旨在解决 npm 和 yarn 的一些效率和资源管理问题。

  • 核心:
  1. 采用内容寻址存储系统:

pnpm 使用内容寻址(content-addressable storage)来存储依赖包。每个依赖包都会被哈希处理,并根据其内容生成唯一的存储地址。这样,即使多个项目依赖于相同版本的包,pnpm 也只需要存储一份,不会重复存储同样内容的文件。

  1. 使用硬链接和符号链接共享依赖:

pnpm 通过在 node_modules 中创建硬链接或符号链接(symlink),指向内容寻址存储中实际的依赖包。这样每个项目可以“共享”依赖,而不必为每个项目单独存储依赖包内容。

  • 硬链接(Hard Link) :将文件内容链接到项目文件夹下,不占用额外磁盘空间。
  • 符号链接(Symlink) :为特定版本的包创建路径映射,使项目代码能够准确找到每个依赖包版本的地址。

特点:

  • 优点:
  1. 显著节省磁盘空间
  2. 安装速度快
  3. 更严格的依赖管理
  4. pnpm-lock.yaml 确保依赖版本一致
  • 缺点:
  1. 不兼容一些使用传统 node_modules 结构的工具和插件:

  2. 在 pnpm 中,每个依赖都有自己的隔离路径,某些工具、插件或构建系统可能会假设 node_modules 目录是扁平的,这可能导致兼容性问题。

  3. 与本地开发和测试环境的潜在不兼容:

  4. 有些项目依赖于本地 node_modules 结构,或者需要直接访问 node_modules 中的文件。在 pnpm 使用内容寻址和符号链接时,这可能会导致某些工具无法正常运行。

目录结构

  • 内容寻址存储
.pnpm-store/
└── v3/└── files/├── 00/                              # 前两位哈希值作为目录名│   └── deadbeef...                  # 包内容的哈希值└── ff/└── cafebabe...                  # 另一个包的哈希值
  • 依赖结构
node_modules/
├── .pnpm/
│   ├── react@17.0.2/
│   │   └── node_modules/
│   │       ├── react/                       # 实际文件(硬链接到 store)
│   │       └── loose-envify/                # react 的依赖
│   └── lodash@4.17.21/
│       └── node_modules/
│           └── lodash/                      # 实际文件(硬链接到 store)
├── react -> .pnpm/react@17.0.2/node_modules/react                 # 符号链接
└── lodash -> .pnpm/lodash@4.17.21/node_modules/lodash             # 符号链接

目录说明

  • .pnpm/ 文件夹:存放项目的所有依赖包,按 包名@版本号 命名,并在其 node_modules 文件夹中包含该包的实际文件和依赖项。

  • 硬链接:.pnpm 中的实际文件并不是直接复制到每个项目中,而是通过硬链接指向 pnpm 的全局缓存存储目录 (pnpm store)。这样,不同项目间的相同版本依赖不需要重复下载。

  • 符号链接:pnpm 会在项目的 node_modules 根目录创建符号链接,将每个包链接到 .pnpm 中实际的包路径。例如:

  • node_modules/react 是一个符号链接,指向 .pnpm/react@17.0.2/node_modules/react
    node_modules/lodash 符号链接指向 .pnpm/lodash@4.17.21/node_modules/lodash

工作原理

  • 包安装:pnpm 会将依赖包下载到全局缓存 (pnpm store) 中,并将实际文件硬链接到 .pnpm 文件夹中的特定版本目录下。
  • 创建符号链接:在项目的 node_modules 文件夹内创建符号链接,将包名称指向 .pnpm 中的对应路径。
  • 引用:项目中的 require(‘react’) 会自动找到 node_modules/react 符号链接,并通过符号链接访问实际文件。

总结:

三者同异:

在这里插入图片描述

使用选择:

基于这些特点:

  • 如果项目体积较小,团队成员 Node.js 经验不同,推荐使用 npm
  • 如果需要更好的性能和可靠性,推荐使用 yarn
  • 如果需要最严格的依赖管理、最小的磁盘空间占用,推荐使用 pnpm

常用命令:

npm、yarn 和 pnpm 的常用命令对比表:
在这里插入图片描述

相关文章:

npm, yarn, pnpm之间的区别

前言 在现代化的开发中,一个人可能同时开发多个项目,安装的项目越来越多,所随之安装的依赖包也越来越臃肿,而且有时候所安装的速度也很慢,甚至会安装失败。 因此我们就需要去了解一下,我们的包管理器&#…...

静态链接和动态链接的特点

静态链接 链接方式‌:在编译时,所有依赖的库代码被直接打包到生成的可执行文件中。这意味着在程序运行时,不需要再加载任何外部库文件‌。 优点‌: 独立性强‌:生成的可执行文件可以在没有依赖库的系统上直接运行&am…...

Mac曲线救国实现Bandizip右键一级菜单

一、前言 个人认为:Bandizip是Mac上最好用的压缩软件,没有之一。 在Mac系统上,学习版的Bandizip由于签名检验问题无法在访达右键的一级菜单显示 解压相关菜单。 有能力的,希望还是支持正版,找找优惠渠道应该100左右。…...

进度与预算

一个项目,如果进度上可以按时完成,一般来说预算不会超标,或者超标幅度有限。 一个项目,如果进度上严重超期,预算基本上会超标,而且超标很大。 现在很多项目,人力成本占比都比较大&#xff0c…...

【教程】创建NVIDIA Docker共享使用主机的GPU

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 这套是我跑完整理的。直接上干货,复制粘贴即可! # 先安装toolkit sudo apt-get update sudo apt-get install -y ca-certifica…...

CEEMDAN-CPO-VMD二次分解(CEEMDAN+冠豪猪优化算法CPO优化VMD)

CEEMDAN-CPO-VMD二次分解(CEEMDAN冠豪猪优化算法CPO优化VMD) 目录 CEEMDAN-CPO-VMD二次分解(CEEMDAN冠豪猪优化算法CPO优化VMD)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 首先运用CEEMDAN对数据进行一次分解&#xff…...

图论理论基础和存储方式的实现

图论1 图论 (Graph theory) 是数学的一个分支,图是图论的主要研究对象。图 (Graph) 是由若干给定的顶点及连接两顶点的边所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系。顶点用于代表事物,连接两顶点的边则用于表示两个事物…...

【实分析】【二】2.2 (c)自然数的序

文章目录 前言一、自然数的序的定义二、自然数的序的基本性质三、序的三歧性四、强归纳法原理总结 前言 在2.2 (b)的末尾,我们定义了自然数的正性,现在,我们来定义自然数的序,它是一种自然数的二元关系,通过加法进行定…...

STM32串口接收与发送(关于为什么接收不需要中断而发生需要以及HAL_UART_Transmit和HAL_UART_Transmit_IT的区别)

一、HAL_UART_Transmit和HAL_UART_Transmit_IT的区别 1. HAL_UART_Transmit_IT(非阻塞模式): HAL_UART_Transmit_IT 是非阻塞的传输函数,也就是说,当你调用 HAL_UART_Transmit_IT 时,它不会等到数据完全发…...

k8s 之storageclass使用nfs动态申请PV

文章目录 配置角色权限部署nfs-client-provisioner创建 NFS StorageClass创建 PVC 来动态申请 PV在 Pod 中使用 PVC验证存储是否正确挂载使用 kubectl 和 jq 筛选 PVCwaiting for a volume to be created, either by external provisioner "nfs-diy" or manually cre…...

vue移动端实现下载(截图)功能

前言 通过html2canvas实现截图功能然后保存 简介 html2canvas库允许我们直接在浏览器上拍摄网页或部分网页的“截图”,即浏览器实现截图的功能。 原理 屏幕截图是基于DO的。其基本原理就是读取已经渲染好的DOM元素的结构和样式信息,然后基于这些信息…...

【Golang】Golang基础语法之面向对象:结构体和方法

面向对象——结构 Go 仅支持封装,不支持继承和多态;继承和多态要做的事情交给接口来完成,即——面向接口编程。Go 只有 struct,没有 class。 定义一个最简单的树节点(treeNode)结构,方法如下&…...

【西门子PLC.博途】——在S71200里写时间设置和读取功能块

之前我们在这篇文章中介绍过如何读取PLC的系统时间。我们来看看在西门子1200里面有什么区别。同时也欢迎关注gzh。 我们在S71200的帮助文档中搜索时间后找到这个数据类型 在博途中他是一个结构体,具体为 然后我们再看看它带的读取和写入时间块 读取时间&#xff1…...

位运算(一)位运算简单总结

191. 位1的个数 给定一个正整数 n,编写一个函数,获取一个正整数的二进制形式并返回其二进制表达式中 设置位 的个数(也被称为 汉明重量)。 示例 1: 输入:n 11 输出:3 解释:输入的二…...

工厂方法模式的理解和实践

在软件开发中,设计模式是一种经过验证的解决特定问题的通用方案。工厂方法模式(Factory Method Pattern)是创建型设计模式之一,它提供了一种创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推…...

C# 设计模式--观察者模式 (Observer Pattern)

定义 观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。观察者模式的核心在于解耦主题(被观察者)和观察者之间的依赖关系。 …...

【开发语言】层次状态机(HSM)介绍

层次状态机(Hierarchical State Machine, HSM),从基本原理、结构设计、实现方法以及如何结合 Qt 进行具体实现等方面进行分析。 1. 层次状态机的基本原理 层次状态机是一种用于管理复杂系统行为的状态机模型,它通过将状态组织成…...

03-13、SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel

SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel 一、Sentinel概述 1、Sentinel是什么 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保…...

【k8s 深入学习之 event 聚合】event count累记聚合(采用 Patch),Message 聚合形成聚合 event(采用Create)

参考 15.深入k8s:Event事件处理及其源码分析 - luozhiyun - 博客园event 模块总览 EventRecorder:是事件生成者,k8s组件通过调用它的方法来生成事件;EventBroadcaster:事件广播器,负责消费EventRecorder产生的事件,然后分发给broadcasterWatcher;broadcasterWatcher:用…...

leetcode104.二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3示例 2: 输入:root [1,null,2] 输出…...

蓝桥杯2117砍竹子(简单易懂 包看包会版)

问题描述 这天, 小明在砍竹子, 他面前有 n 棵竹子排成一排, 一开始第 i 棵竹子的 高度为 hi​. 他觉得一棵一棵砍太慢了, 决定使用魔法来砍竹子。魔法可以对连续的一 段相同高度的竹子使用, 假设这一段竹子的高度为 H, 那么 用一次魔法可以 把这一段竹子的高度都变为 ⌊H2⌋…...

LCD与lvgl

LCD与lvgl 目录 LCD与lvgl 回顾 LCD 的驱动层讲解 1、LCD 的常见接口 2、我们的 LCD 的参数 3、LCD 的设备树说明 4、LCD 的设备树说明 5、如何移植 LCD 的驱动(重点) LCD 的应用层开发 1:LCD 应用开发->界面开发的方法 2:LVGL 模拟器安装 3:LVGL 工程创建和…...

SpringBoot 赋能:精铸超稳会员制医疗预约系统,夯实就医数据根基

1绪论 1.1开发背景 传统的管理方式都在使用手工记录的方式进行记录,这种方式耗时,而且对于信息量比较大的情况想要快速查找某一信息非常慢,对于会员制医疗预约服务信息的统计获取比较繁琐,随着网络技术的发展,采用电脑…...

android studio 读写文件操作(应用场景二)

android studio版本:2023.3.1 patch2 例程:readtextviewIDsaveandread 本例程是个过渡例程,如果单是实现下图的目的有更简单的方法,但这个方法是下一步工作的基础,所以一定要做。 例程功能:将两个textvi…...

小尺寸低功耗蓝牙模块在光伏清扫机器人上的应用

一、引言 随着可再生能源的迅速发展,光伏发电系统的清洁与维护变得越来越重要。光伏清扫机器人通过自动化技术提高了清洁效率,而蓝牙模组的集成为这些设备提供了更为智能的管理和控制方案。 二、蓝牙模组的功能与实现: 蓝牙模组ANS-BT103M…...

防火墙有什么作用

防火墙的作用:1. 提供网络安全防护;2. 实施访问控制和流量过滤;3. 检测和阻止恶意攻击;4. 保护内部网络免受未经授权的访问;5. 监控网络流量和安全事件;6. 支持虚拟专用网络(VPN)。防…...

MongoDB-BSON 协议与类型

前言: MongoDB 是一个高性能、无模式的 NoSQL 数据库,广泛应用于大数据处理和实时数据存储。作为一个数据库系统,MongoDB 的核心之一就是其使用的 BSON(Binary JSON)格式,它用于存储数据以及在客户端和数据…...

学习threejs,使用VideoTexture实现视频Video更新纹理

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️VideoTexture 视频纹理 二、…...

怎么获取键值对的键的数值?

问: 通过paelData.cardMap.C0002112可以获取到Cooo2112里面的数据,但是有时候接口返回的不是C0002112而是C0002093或者其他值,请问我该怎么写? 后端返回的数据是这样的: cardMap: { C0002112: { name: Item 1, va…...

数据结构排序算法详解

数据结构排序算法详解 1、冒泡排序(Bubble Sort)2、选择排序(Selection Sort)2、插入排序(Insertion Sort) 1、冒泡排序(Bubble Sort) 原理:越小的元素会慢慢“浮”到数…...