npm依赖结构解析

npm 基本命令

npm install 命令用来安装依赖到node_modules目录。

当执行这条命令的时候npm会先去查看当前node_modules里面是否已经有这个包的,如果没有才会进行安装。

go 复制代码
npm install <package name>

如果你希望node_modules无论什么情况下都要安装,那么你可以安装的时候加上一个-f的参数

go 复制代码
npm install <package name> -f

如果你想更新已安装的某个包的版本,你可以用如下命令

go 复制代码
npm update <package name>

npm包的发布

npm包版本后缀

sql 复制代码
Alpha 代表内部测试版本
Beta 代表公开的广泛测试版本
RC 代表修复问题和缺陷的候选版本
Release 代表最终的可靠版本

npm的发展过程

npm2--嵌套地狱

早期的时候,npm安装依赖的时候是完全按照层级关系去嵌套安装的。这会导致嵌套地狱的产生。

比如:A@1.0.0的依赖是:B@1.0.0,C@1.0.0的依赖是:B@1.0.0、D@1.0.0。那么npm安装之后结构就是下面这种情况。

kotlin 复制代码
node_modules
├── A@1.0.0
│   └── node_modules
│       └── B@1.0.0
└── C@1.0.0
    └── node_modules
        └── B@1.0.0
        └── D@1.0.0

这种嵌套方式虽然结构清晰明了,但是多个依赖如果都依赖同个包的话,这个包会被下载多次,会导致node_modules的体积过于庞大。

npm3--扁平化嵌套、不确定性、幽灵依赖

扁平化嵌套

针对npm2嵌套地狱的问题,npm3作出了解决方案,就是采用扁平化嵌套方式。

无论是直接依赖还是子依赖的依赖,优先安装到node_modules的根目录下。当安装到相同模块的时候,会判断已安装模块是否符合新模块的版本范围,如何符合则跳过安装,不符合则安装在当前模块的node_modules下面。

比如:项目依赖了A、C、D,A依赖B@v1,C依赖B@v2,D依赖E@v1。那么npm安装之后结构就是下面这种情况

css 复制代码
node_modules
├── A
├── B@v1
├── C
   └── node_modules
        └── B@v2
├── D
└──E@v1

这种嵌套方案可以避免同个包被安装多次,也可以避免嵌套层级太深。但是它又会产生新的问题:幽灵依赖、不确定性。

幽灵依赖

比如:项目依赖了A、C、D,A依赖B@v1,C依赖B@v2,D依赖E@v1。那么npm安装之后结构就是下面这种情况

css 复制代码
node_modules
├── A
├── B@v1
├── C
   └── node_modules
        └── B@v2
├── D
└──E@v1

有些时候,我们的项目根本没有直接依赖B@v1,但是因为扁平化,导致了间接依赖B@v1被提升到了顶级,我们在项目中就可以直接导入B@v1进行使用。但是如果后面我们删除了A包,那么node_modules结构就会变成下面这种结构,那我们之前导入B@v1的那段代码就会报错。

css 复制代码
node_modules
├── C
├──B@v2
├── D
└──E@v1

node_modules结构的不确定性

因为存在依赖提升,所以node_modules的结构根据安装包的顺序不同,可能会发生变化,举例如下:

假如我目前在准备项目的一些前置依赖,然后我的安装顺序如下:

A -> C(依赖lodash@3) -> B(依赖lodash@2),那么我的node_modules的结构如下

css 复制代码
node_modules
├── A
├──lodash@3
├── B
   └── node_modules
        └── lodash@2
└── C

但是package.json是按照字典排序将这些包添加在依赖字段中的。下一个开发者如果按照pakcage.json去安装,那么他的安装顺序和node_modules结构如下:

A -> B(依赖lodash@2) -> C(依赖lodash@3)

css 复制代码
node_modules
├── A
├──lodash@2
├── B
└── C
    └── node_modules
        └── lodash@3

从上面就可以看出两者node_modules结构的不确定性

package-lock.json文件的解析

假如有这么一个package.json,文件内容如下

json 复制代码
{
  "name": "my-app",   // 包的名字
  "version": "1.0.0", // 包的版本
  "dependencies": {   // 包的运行依赖
    "base64-js": {    // 所依赖的包1
      "version": "1.0.1", // 包的版本
      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.1.tgz", // 包的安装来源
      "integrity": "sha1-aSbRsZT7xze47tUTdW3i/Np+pAg="
    },
    "buffer": {
      "version": "5.4.3",
      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz",
      "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==",
      "requires": { // buffer的依赖
        "base64-js": "^1.0.2",
        "ieee754": "^1.1.4"
      },
      "dependencies": { // buffer的依赖,真正安装在buffer的node_modules下面的依赖
        "base64-js": {
          "version": "1.3.1",
          "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
          "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
        }
      }
    },
    "ieee754": {
      "version": "1.1.13",
      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
    },
    "ignore": {
      "version": "5.1.4",
      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
      "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A=="
    }
  }
}

为了解决npm install的不确定性的问题,npm5引进了package-lock.json。只要项目中有package-lock.json,所有开发者都可以通过npm install按照package-lock.json的规则去安装包,而package-lock.json的dependencies可以解决这种不确定性。但是这依然无法解决幽灵依赖问题

参考文章:
npm 模块安装机制简介
彻底了解npm------架构、进化史及原理解析
聊聊依赖管理
前端工程化 - 剖析npm的包管理机制

npm的进化史

相关推荐
晴天161 天前
Node.js 中 `npm install` 命令分析-Day30
前端·npm·node.js
_Rookie._2 天前
Package.json 的imports 和 exports
npm
泠曦れいひ2 天前
在当前项目中安装Vite
前端框架·npm·node.js
Joseph 乔2 天前
【NVM】node 和 nvm 卸载(安装 node & nvm & npm & cnpm & yarn 及配置)
npm·node.js
frjc2 天前
Node.js 与 npm 极简安装教程
前端·npm·node.js
北漂燕郊杨哥5 天前
npm 缓存占满 C 盘?三步把它迁到 D 盘(附验证与避坑)
缓存·npm·node
必须会一定会5 天前
Agent Handoff M5 发布验收:`CHANGES.md`、`npm pack`、`release:check` 与干净环境安装验证
前端·人工智能·npm·node.js·ai编程
剑胆琴心静水深流6 天前
全栈之路6---web集成与呈现
前端·vue.js·spring boot·分布式·spring·前端框架·npm
章鱼小丸子逃跑中6 天前
【2025最新版】如何将fnm与node.js安装在D盘?【保姆级安装及人性话理解教程】
前端·javascript·npm·node.js
qq_452396237 天前
第三篇:《包管理进化史:从 npm 到 pnpm,Monorepo 时代的依赖管理》
前端·npm·node.js