果断放弃npm切换到pnpm--节约磁盘空间(256G硬盘救星)

2023-12-07 23:10

本文主要是介绍果断放弃npm切换到pnpm--节约磁盘空间(256G硬盘救星),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

团队成立初期我们采用 npm3 来管理项目依赖,后续我们研发了自己组件库、图表库、工具库,采用了 monorepo 管理,依赖管理也由 npm3 切换成了 yarn(yarn workspace)。不管是 npm3 还是 yarn 都采用扁平化的 node_modules 文件夹方式,以此避免引入层级过深、相同依赖版本重复等问题。

随着公司业务不断壮大,团队支撑的项目越来越多。由于依赖是跟随项目的,导致磁盘空间占用严重。

由于上述原因,开始尝试使用 pnpm 来进行管理。

节约磁盘空间

pnpm 依赖项将存储在一个全局内容可寻址的仓库中(${os.homedir}/.pnpm-store),具体项目中使用依赖采用硬链接方式,而不是进行复制。对于每个模块的每个版本只保留一个副本。如:本地有10个项目依赖相同 vue 版本,如果使用 npm 或 yarn 时本地磁盘需要有 10 个 vue 的副本;而 pnpm 只有1个。

  1. 如果你用到了某依赖项的不同版本,那么只会将有差异的文件添加到仓库(公共仓库)。
  2. 所有文件都会存储在硬盘上的同一位置。 当多个包(package)被安装时,所有文件都会从同一位置创建硬链接,不会占用额外的磁盘空间。 这允许跨项目共享同一版本的依赖。
$ pnpm installPackages: +1585
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Packages are hard linked from the content-addressable store to the virtual store.Content-addressable store is at: /Users/ligang/.pnpm-store/v3Virtual store is at:             node_modules/.pnpm
Progress: resolved 1585, reused 1585, downloaded 0, added 1585, done

可以发现:

  • 内容可寻址存储在 /Users/ligang/.pnpm-store/v3
  • 虚拟存储目录 node_modules/.pnpm
  • downloaded 0,这样极大的提升了 install 速度
ll node_moduleslrwxr-xr-x   1 ligang  staff    44B  9  1 17:59 deepmerge -> .pnpm/deepmerge@3.3.0/node_modules/deepmerge
lrwxr-xr-x   1 ligang  staff    72B  9  1 17:59 element-resize-detector -> .pnpm/element-resize-detector@1.2.2/node_modules/element-resize-detector
lrwxr-xr-x   1 ligang  staff    58B  9  1 17:59 element-ui -> .pnpm/element-ui@2.13.1_vue@2.6.12/node_modules/element-ui
lrwxr-xr-x   1 ligang  staff    39B  9  1 17:59 eslint -> .pnpm/eslint@5.16.0/node_modules/eslint

node_modules 目录下的文件全部被软链到了虚拟存储路径下 .pnpm.pnpm/ 以平铺的形式储存着所有的包(格式:.pnpm/<name>@<version>/node_modules/<name>)。.pnpm 目录下的包会硬链到全局仓库中(/Users/ligang/.pnpm-store/v3)。

关于「硬链」、「软链」可以查看上篇博文。

以项目中依赖 element-ui 为例:

cd node_modulesls -li element-ui
8643474522 lrwxr-xr-x  1 ligang  staff  58  9  1 17:59 element-ui -> .pnpm/element-ui@2.13.1_vue@2.6.12/node_modules/element-uils -li .pnpm/element-ui@2.13.1_vue@2.6.12/node_modules/
8643424956 drwxr-xr-x  13 ligang  staff  416  9  1 17:59 element-ui

node_modules 目录下,element-ui软链到了 .pnpm 对应的目录下 element-ui;.pnpm目录下,element-ui硬链接( link count 13)。

非扁平化的 node_modules 文件夹

回归一下 node_modules 结构历史:

第一阶段:npm@3 之前版本

node_modules
└─ foo├─ index.js├─ package.json└─ node_modules└─ bar├─ index.js└─ package.json
  • 依赖树层级太深,会导致 Windows 上的目录路径过长问题
  • 相同包在不同的依赖项中需要时,会存在多个相同副本

第二阶段:npm@3 版本,扁平化处理

主要是解决上述两个问题

node_modules
├─ foo
|  ├─ index.js
|  └─ package.json
└─ bar├─ index.js└─ package.json

第三阶段:pnpm

由于扁平化算法的极其复杂,以及会存在多项目间相同依赖副本的情况。pnpm 在尝试解决这些问题时,放弃了扁平化处理 node_modules 的方式。而是采用 硬链+软链 方式。

node_modules
├─ .pnpm
|  ├─ foo@1.0.0/node_modules/foo
|  |  └─ index.js
|  └─ bar@2.0.0/node_modules/bar
├─ foo -> .pnpm/foo@1.0.0/node_modules/foo
└─ bar -> .pnpm/bar@2.0.0/node_modules/bar

node_modules 根目录中的包只是一个符号链接。require('foo') 将执行 node_modules/.pnpm/foo@1.0.0/node_modules/foo/indexjs 中的文件(这里是硬链接),而不是 node_modules/foo/index.js 中的文件。

好处

这种布局结构的一大好处是只有真正在依赖项中(package.json dependences)的包才能访问。使用扁平化的 node_modules 结构,所有提升的包都可以访问。

npm@3/yarn 采用扁平化的方式管理 node_modules

示例:以 chokidar 为例

"dependencies": {"chokidar": "^3.5.2"
}

项目中依赖了 chokidar 用于监听文件夹内容变化,通过 npm 安装后结构
在这里插入图片描述
依赖包如此之多,正是由于扁平化处理而来。chokidar 依赖包以及其依赖的依赖包都被提取到了一级目录下。这种方式会导致没有明确被依赖的包也可以被引用。
在这里插入图片描述

const isNumber = require('is-number')
console.log(isNumber(123), isNumber('abc'))

上述可以正常引用到!

采用 pnpm 重新安装
在这里插入图片描述
执行上面代码,会报错:Error: Cannot find module ‘is-number’

问题

扁平化 node_modules 导致了上述错误。如果存在这种情况,需要切换成 pnpm 我们应该如何处理?

方案1:

通过 pnpm add <dependencie> 添加依赖

方案2:

通过相关 hooks 添加相关的依赖

.pnpmfile.cjs

module.exports = {hooks: {readPackage: (pkg) => {if (pkg.name === "inspectpack") {pkg.dependencies['babel-traverse'] = '^6.26.0'}return pkg}}
}

方案3:

如果缺少依赖太多,可以使用提升选项。此选项官方不推荐。

pnpm install --shamefully-hoist

由于 cli3 对于 pnpm 支持不够完善(在 cli4 中已完全支持),我们采用了这种方式。 相关 Issue

总结

pnpm 方式的实现精髓

  1. 通过软链的形式,使得 require 可以正常引用;同时对非真正依赖的项目做隔离(避免引用依赖的混乱)
  2. .pnpm 的存在避免了循环引用和层级过深的问题(都在其第一层)
  3. 硬链使得不同项目相同依赖只存在一个副本,减少磁盘空间

参考链接

  • https://www.kochan.io/nodejs/pnpms-strictness-helps-to-avoid-silly-bugs.html
  • https://www.kochan.io/nodejs/why-should-we-use-pnpm.html
  • https://github.com/vuejs/vue-cli/issues/2703
  • https://pnpm.io/zh/faq

这篇关于果断放弃npm切换到pnpm--节约磁盘空间(256G硬盘救星)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/467672

相关文章

MyBatis 切换不同的类型数据库方案

下属案例例当前结合SpringBoot 配置进行讲解。 背景: 实现一个工程里面在部署阶段支持切换不同类型数据库支持。 方案一 数据源配置 关键代码(是什么数据库,该怎么配就怎么配) spring:datasource:name: test# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource# @需要修改 数据库连接及驱动u

解决Office Word不能切换中文输入

我们在使用WORD的时可能会经常碰到WORD中无法输入中文的情况。因为,虽然我们安装了搜狗输入法,但是到我们在WORD中使用搜狗的输入法的切换中英文的按键的时候会发现根本没有效果,无法将输入法切换成中文的。下面我就介绍一下如何在WORD中把搜狗输入法切换到中文。

15 组件的切换和对组件的data的使用

划重点 a 标签的使用事件修饰符组件的定义组件的切换:登录 / 注册 泡椒鱼头 :微辣 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-

71-java 导致线程上下文切换的原因

Java中导致线程上下文切换的原因通常包括: 线程时间片用完:当前线程的时间片用完,操作系统将其暂停,并切换到另一个线程。 线程被优先级更高的线程抢占:操作系统根据线程优先级决定运行哪个线程。 线程进入等待状态:如线程执行了sleep(),wait(),join()等操作,使线程进入等待状态或阻塞状态,释放CPU。 线程占用CPU时间过长:如果线程执行了大量的I/O操作,而不是CPU计算

win7如何设置SATA硬盘

Win7在安装时设置的是IDE,安装完后需要在注册表中设置为SATA,否则直接设BIOS会不认硬盘,具体如下 注册表子项:HKEY_LOCAL_MACHINE/System/CurrentControlSet/Services/Msahci 找到Start键,将值0改为3

有关机械硬盘的基础知识

1,机械硬盘的品牌   目前市场中常见的笔记本电脑的机械硬盘品牌主要有希捷、西部数据、三星等。   2,机械硬盘的容量   硬盘容量,即硬盘所能存储的最大数据量。虽然笔记本电脑硬盘的容量会因单位密度的提升而增加,不过和台式电脑的大容量比起来,笔记本电脑硬盘的容量仍然落后许多。笔记本电脑的硬盘除了对磁盘有体积较小和数量较少的要求之外,对功耗、耐用程度、抗震性及成本等的考虑,也让笔记

[轻笔记]ubuntu shell脚本切换conda环境

source /home/yourhostname/anaconda3/etc/profile.d/conda.sh # 关键!!!conda activate env_name

ViewPager+fragment实现切换页面(一)

如今的很多应用中都是下面有一排按钮,点击可以切换页面,滑动也可以切换页面。下面就来简单的实现这个功能。 思路 首先肯定是会用到viewpager这个控件,为了能够向下兼容,最好用v4包下的viewpager,Activity要继承FragmentActivity 其次用一个集合来存储所有的fragment页面在设置viewpager的适配器时,把存储fragment页面的list集合传入ada

『功能项目』武器的切换实例【34】

本章项目成果展示 我们打开上一篇33战士的A键连击的项目, 本章要做的事情是按键盘E键切换职业时切换手中的武器 首先在资源商店下载免费的武器模型 创建一个空物体 命名为WeaponPos 将武器预制体拖拽至WeaponPos (注意调整空物体位置就可以后续文章会更换武器) 隐藏两把武器 运行项目 隐藏装备 在资源商店

2409wtl,切换视图

原文 介绍 我从一个基于SDI(单文档接口)WTL向导的应用开始,添加了一些从控件继承的窗口和一些对话框窗口(表单视图),然后才发现我必须,使SDI框架动态加载和卸载子窗口. 本文演示了两个可用来完成的技术:在SDI应用中的视图间动态切换.这是我使用的两个. 技术 1技术:第一个方法涉及按需析构和重建视图实例.这更简单,且在不介意析构和重建窗口对象时效果很好. 2:按需创建视图,然后用