pnpm、npm、yarn 包管理工具『优劣对比』及『环境迁移』

news2025/1/11 17:58:32

前言

博主在开发前端网站的时候,发现随着开发的项目的逐渐增多,安装的依赖包越来越臃肿,依赖包的安装速度也是非常越来越慢,多项目开发管理也是比较麻烦。之前我就了解过 pnpm,但是当时担心更换包管理环境可能会出现的依赖等问题,并且也没有急切的需求,所以当时并没有立即更换

综上所述,随着上面问题的出现,更换包管理环境也逐渐提上日程,所以本文主要将会简单对比 pnpm 和 npm / yarn ,并且详细讲解如何在多项目环境中迁移到 pnpm

Pnpm安装与使用教程- 小白龙博客

介绍

npm v2

此时期主要是采用简单的递归依赖方法,最后形成高度嵌套的依赖树。然后就会造成如下问题:重复依赖嵌套地狱,空间资源浪费,安装速度过慢,文件路径过长等问题。大家都很熟悉,这里不再详细解释

npm v3

v3 版本作了较大的更新,开始采取扁平化的依赖结构。这样的依赖结构可以很好的解决重复依赖的嵌套地狱问题,但是却出现扁平化依赖算法耗时长这样新的问题

官方仓库 issue 的解释:npm@3 wants to be faster · Issue #8826 · npm/npm (github.com)

npm v5

为了解决上面出现的扁平化依赖算法耗时长问题,npm 引入 package-lock.json 机制,package-lock.json 的作用是锁定项目的依赖结构,保证依赖的稳定性,有兴趣的朋友可以直接查看官方文档

官方文档:package.json | npm Docs (npmjs.com)

注:其实在 package-lock.json 机制出现之前,可以通过 npm-shrinkwrap 实现锁定依赖结构,但是 npm-shrinkwrap 默认关闭,需要主动执行。

yarn

官网:Home page | Yarn (yarnpkg.com)

首先需要提出的是 yarn 出现时间为 2016 年,此时 npm 处于 v3 时期,其实当时 yarn 解决的问题基本就是 npm v5 解决的问题,包括使用 yarn.lock 等机制,锁定版本依赖,实现并发网络请求,最大化网络资源利用率,其次还有利用缓存机制,实现了离线模式

其实后面很多 npm 都是在学习 yarn 的机制,上面的机制目前 npm 基本也都实现了,就目前而言 npm 和 yarn 其实并没有差异很大,具体使用 npm 还是 yarn 可以看个人需求

pnpm

中文官网:pnpm - 速度快、节省磁盘空间的软件包管理器 | pnpm中文文档 | pnpm中文网

pnpm 内部使用基于内容寻址的文件系统来存储磁盘上所有的文件,这样可以做到不会出现重复安装,在项目中需要使用到依赖的时候,pnpm 只会安装一次,之后再次使用都会直接硬链接指向该依赖,极大节省磁盘空间,并且加快安装速度

注:硬链接是多个文件名指向同一个文件的实际内容,而软链接(符号链接)是一个独立的文件,指向另一个文件或目录的路径

也许有人说 yarn 默认也是扁平化安装方式,但是 yarn 有独特的 PnP 安装方式,可以直接去掉 node_modules,将依赖包内容写在磁盘,节省了 node 文件 I/O 的开销,这样也能提升安装速度,但是 yarn PnP 和 pnpm 机制是不同的,且总体来说安装速度 pnpm 是要快于 yarn PnP 的,详情请看下面官方文档

官方文档:Overview | Yarn (yarnpkg.com)

最后就是 pnpm 是默认支持 monorepo 多项目管理的,在日渐复杂的前端多项目开发中尤其适用,也就说我们不再需要 lerna 来管理多包项目,可以使用 pnpm + Turborepo 作为我们的项目管理环境

配置工作空间官方文档:工作空间(Workspace) | pnpm

img

还有就是 pnpm 还能管理 nodejs 版本,可以直接替代 nvm,命令如下所示

# 安装 LTS 版本
pnpm env use --global lts
# 安装指定版本
pnpm env use --global 16

迁移

迁移过程中主要有如下问题:因为使用 npm 或 yarn 安装依赖项时,所有包都被提升到模块目录的根目录。因此,源代码可以访问未作为依赖项添加到项目的依赖项。但是默认情况下,pnpm 使用链接仅将项目的直接依赖项添加到模块目录的根目录中

这意味着如果 package.json 没有引用的依赖,那么它将无法解析。这是迁移中的最大障碍。可以使用 auto-install-peers 设置自动执行此操作(默认情况下是false)

对于多个使用 npm 安装依赖的项目,单独删除依赖包很耗时间,我们可以使用 npkill ,该工具可以列出系统中的任何 node_modules 目录以及它们占用的空间。然后可以选择要删除的依赖以释放空间

npkill-demo-0.10.0

首先全局安装包

npm i -g pnpm

迁移步骤如下

1.首先使用 npkill 删除 node_modules 依赖包

2.项目根目录创建 .npmrc,填写如下内容

auto-install-peers=true

3.导入依赖锁定文件(pnpm-lock.yaml)

保证根目录有如下依赖锁定文件(npm-shrinkwrap.json,package-lock.json,yarn.lock)

然后执行如下命令

pnpm import pnpm-lock.yaml

4,最后执行 pnpm i 安装依赖

问题

生成依赖文件警告

官方 issue 解释: Unmet peer dependencies and The command – pnpm/pnpm (github.com)

生成 pnpm-lock.yaml 文件时出现如下警告

 WARN  Issues with peer dependencies found
.
└─┬ vuepress 1.9.9
  └─┬ @vuepress/core 1.9.9
    └─┬ vue-loader 15.10.1
      └─┬ @vue/component-compiler-utils 3.3.0
        └─┬ consolidate 0.15.1
          ├── ✕ unmet peer react-dom@^16.13.1: found 15.7.0
          └── ✕ unmet peer react@^16.13.1: found 15.7.0

这是因为在 npm 3 中,不会再强制安装 peerDependencies (对等依赖)中所指定的包,而是通过警告的方式来提示我们。pnpm 会在全局缓存已经下载过的依赖包,如果全局缓存的依赖版本与项目 package.json 中指定的版本不一致,就会出现这种 hint 警告

我们可以在项目的 package.json 中配置 peerDependencyRules 忽略对应的警告提示

{
  "pnpm": {
    "peerDependencyRules": {
      "ignoreMissing": [
        "react"
      ]
    }
  }
}

或者说直接在 .npmrc 配置文件中直接关闭严格的对等依赖模式,可以添加 strict-peer-dependencies=false 到配置文件中,或者执行如下命令

npm config set strict-peer-dependencies=false

然后也可能会出现警告 deprecated subdependencies found,暂时可以忽略

幽灵依赖问题

在最后安装依赖的时候可能会出现幽灵依赖问题,幽灵依赖就是没有在package.json中,但是项目中,或者引用的包中使用到的依赖

举个例子,比如我们现在使用 npm 安装了 v-viewer 依赖,同时 viewerjsv-viewer 的依赖项,由于扁平化依赖机制,我们可以在 node_modules/v-viewer/package.json 中看到声明的 viewerjs 依赖,即使项目根目录下的 package.json 没有声明 viewerjs 依赖,我们仍旧可以使用,这就是幽灵依赖

而现在我们切换为 pnpm 后,在默认情况下不允许访问未声明的依赖,有以下两种解决方案

1.自行安装未声明依赖项

幽灵依赖自动扫描工具:@sugarat/ghost - npm (npmjs.com)

pnpm i -S viewerjs

或者说某些版本 pnpm 会自动爆出幽灵依赖错误 missing peer ...,也可以直接不使用上面的扫描工具,直接自行安装后面的 ... 依赖

2.找到.npmrc 文件,在其中配置 public-hoist-pattern 或者 shamefully-hoist 字段,将依赖提升到根node_modules 目录下解决,也就是所谓的依赖提升

依赖提升官方文档:.npmrc | pnpm

# .npmrc
# 提升含有 eslint(模糊匹配)、prettier(模糊匹配)、viewerjs(精确匹配) 的依赖包到根 node_modules 目录下
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
public-hoist-pattern[]=viewerjs

# 提升所有依赖到根 node_modules 目录下,相当于 public-hoist-pattern[]=*,与上面一种方式一般二选一使用
shamefully-hoist=true

当然,极不推荐用这样的方式解决依赖问题,这样没有充分利用 pnpm 依赖访问安全性的优势,又走回了 npm / yarn 的老路。

参考链接

  • pnpm 对比 npm/yarn 好在哪里 - 掘金 (juejin.cn)
  • 现在我更推荐 pnpm 而不是 npm/yarn? - 掘金 (juejin.cn)
  • 如何将 npm / yarn 项目迁移到 pnpm? - 掘金 (juejin.cn)
  • 浅谈 npm, yarn, pnpm之间的区别 - 掘金 (juejin.cn)
  • vue 配套生态已经全面使用 pnpm 了 - 掘金 (juejin.cn)
  • 打造高效Monorepo:Turborepo、pnpm、Changesets实践 – UU跑腿技术团队 (uupt.com)
  • 新一代包管理工具 pnpm 使用心得 - 知乎 (zhihu.com)
  • ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies | 天問-专注于大前端技术 (tiven.cn)

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1067897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在微信小程序中如何引入iconfont

目录 前言iconfont 介绍iconfont 组建图标库并下载1. 通过 iconfont 可组建自己的图标库2. 项目设置:生成base64格式的字体3. 下载至本地4. 解压备用 将 iconfont 添加到小程序中1. 将css该名称wxss2. 将文件复制到font目录3. 在app.wxss文件中引入iconfont文件 在小…

Vue中如何进行分布式日志收集与日志分析(如ELK Stack)

在Vue中实现分布式日志收集与日志分析(使用ELK Stack) 日志收集和分析在现代应用程序中是至关重要的,它们可以帮助开发人员监视和诊断应用程序的行为,从而提高应用程序的稳定性和性能。ELK Stack(Elasticsearch、Logs…

矩阵的相似性度量的常用方法

矩阵的相似性度量的常用方法 1,欧氏距离 欧式距离是最易于理解的一种距离计算方法,源自欧式空间中两点间的距离公式。 (1)二维平面上的点 a ( x 1 , y 1 ) a(x_1,y_1) a(x1​,y1​)和点 b ( x 2 , y 2 ) b(x_2,y_2) b(x2​,y2​)的欧式距离为 d ( x …

Langchain-Chatchat项目:3-Langchain计算器工具Agent思路和实现

本文主要讨论Langchain-Chatchat项目中自定义Agent问答的思路和实现。以"计算器工具"为例,简单理解就是通过LLM识别应该使用的工具类型,然后交给相应的工具(也是LLM模型)来解决问题。一个LLM模型可以充当不同的角色&…

Spring AOP(JavaEE进阶系列5)

目录 前言: 1.什么是Spring AOP 2.为什么要使用AOP呢? 3.AOP的组成 3.1切面 3.2切点 3.3通知 3.4连接点 4.Spring AOP的实现 4.1添加依赖 4.2定义切面 4.3定义切点 4.4实现通知 5.AOP的实现原理 结束语: 前言: 在…

光伏VSG-基于虚拟同步发电机的光伏并网逆变器系统(Simulink仿真)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

EfficientNetV2:更快、更小、更高精度

一、说明 EfficientNets是目前最强大的卷积神经网络(CNN)模型之一。随着视觉变压器的兴起,它实现了比高效网络更高的精度,出现了CNN现在是否正在消亡的问题。EfficientNetV2 不仅通过提高准确性,还通过减少训练时间和延…

Linux系统之部署h5ai目录列表程序

Linux系统之部署h5ai目录列表程序 一、h5ai介绍1.1 h5ai简介1.2 h5ai特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、安装httpd软件4.1 检查yum仓库4.2 安装httpd软件4.3 启动httpd服务4.4 查看htt…

rabbitmq查看节点信息命令失败

不影响访问rabbitmq,但是无法使用 命令查看节点信息 等 查看节点信息命令:rabbitmq-diagnostics status --node rabbitJHComputer Error: unable to perform an operation on node ‘rabbitJHComputer‘. Please see diagnostics informatio rabbitmq-…

C++内存管理:其二、数组内存管理

一、new一个对象数组 如果我们想在堆内存上创建一个对象数组,可以这么写: Complex * pca new Complex[3]; 主要做三件事: (1)申请一块内存,足够放置三个Complex对象。 (2)执行三次…

(一)实现一个简易版IoC容器【手撸Spring】

一、前言 相信大家在看本篇文章的时候,对IoC应该有一个比较清晰的理解,我在这里再重新描述下:它的作用就是实现一个容器将一个个的Bean(这里的Bean可以是一个Java的业务对象,也可以是一个配置对象)统一管理…

无需公网IP,教学系统如何实现远程一站式管理维护?

全国多所高校应用红亚科技研发的一套教学实验系统平台,此实验系统服务器分别部署在学校内部,与校内的各种教学资源整合在一起,向校内师生提供服务。 红亚总部设立在北京,虽说在全国22个省会均设有办事处,在面对全国多…

MM-Camera架构-Preview 流程分析

目录 文章目录 1 log开的好,问题都能搞2 lib3 preview3.1 打开视频流3.1.1 cpp\_module\_start\_session3.1.2 cpp\_thread\_create3.1.3 cpp\_thread\_funcsundp-3.1 cpp\_hardware\_open\_subdev(ctrl->cpphw)sundp-3.2 cpp\_hardware\_process\_command(ctrl-…

ssm+vue的劳务外包管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频: ssmvue的劳务外包管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构&…

3BHB003154R0101 GVC707AE01 3BHB003149P201 3BHB003149P104 5SXE05-0156

3BHB003154R0101 GVC707AE01 3BHB003149P201 3BHB003149P104 5SXE05-0156 特征 IT/OT SOC是一种安全监控服务,它使用快速、可扩展和统一的下一代安全信息和事件管理(SIEM)。该服务使用各种CTI和ML工具从客户的IT/OT设备收集事件日志,以检测网络攻击、…

leetCode 300.最长递增子序列 (贪心 + 二分 ) + 图解 + 优化 + 拓展

300. 最长递增子序列 - 力扣(LeetCode) 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如&#xff…

Kafka 高可用

正文 一、高可用的由来 1.1 为何需要Replication 在Kafka在0.8以前的版本中,是没有Replication的,一旦某一个Broker宕机,则其上所有的Partition数据都不可被消费,这与Kafka数据持久性及Delivery Guarantee的设计目标相悖。同时Pr…

算法通关村第17关【青铜】| 贪心

贪心算法(Greedy Algorithm)是一种常见的算法设计策略,通常用于解决一类优化问题。其核心思想是:在每一步选择中都采取当前状态下的最优决策,从而希望最终能够达到全局最优解。贪心算法不像动态规划算法需要考虑各种子…

电暖产品经营小程序商城搭建

电暖产品的需求度很高,包括地暖系统及壁挂炉、水暖散热器等,尤其每年冬天,部分家庭或办公场所就会有相关需求,庞大市场下为电暖各领域商家及品牌带来了商机。 然而随着互联网深入各行业及实体店生意难做,无论品牌还是…

安卓RecycleView包含SeekBar点击列表底部圆形阴影处理

seekbar在列表中点击底部圆形阴影禁止显示方法 大家好,最近写了自定义的seekbar实现显示进度值,然而呢,我的seekbar控件是作为recycleview的item来使用的,我设置了禁止点击和滑动方法如下: seekBar.setOnTouchListene…