果断放弃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

相关文章

JDK多版本共存并自由切换的操作指南(本文为JDK8和JDK17)

《JDK多版本共存并自由切换的操作指南(本文为JDK8和JDK17)》本文介绍了如何在Windows系统上配置多版本JDK(以JDK8和JDK17为例),并通过图文结合的方式给大家讲解了详细步骤,具有... 目录第一步 下载安装JDK第二步 配置环境变量第三步 切换JDK版本并验证可能遇到的问题前提:公司常

nvm如何切换与管理node版本

《nvm如何切换与管理node版本》:本文主要介绍nvm如何切换与管理node版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录nvm切换与管理node版本nvm安装nvm常用命令总结nvm切换与管理node版本nvm适用于多项目同时开发,然后项目适配no

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

Oracle数据库如何切换登录用户(system和sys)

《Oracle数据库如何切换登录用户(system和sys)》文章介绍了如何使用SQL*Plus工具登录Oracle数据库的system用户,包括打开登录入口、输入用户名和口令、以及切换到sys用户的... 目录打开登录入口登录system用户总结打开登录入口win+R打开运行对话框,输php入:sqlp

IDEA如何切换数据库版本mysql5或mysql8

《IDEA如何切换数据库版本mysql5或mysql8》本文介绍了如何将IntelliJIDEA从MySQL5切换到MySQL8的详细步骤,包括下载MySQL8、安装、配置、停止旧服务、启动新服务以及... 目录问题描述解决方案第一步第二步第三步第四步第五步总结问题描述最近想开发一个新应用,想使用mysq

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

Spring Boot实现多数据源连接和切换的解决方案

《SpringBoot实现多数据源连接和切换的解决方案》文章介绍了在SpringBoot中实现多数据源连接和切换的几种方案,并详细描述了一个使用AbstractRoutingDataSource的实... 目录前言一、多数据源配置与切换方案二、实现步骤总结前言在 Spring Boot 中实现多数据源连接

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-