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的进化史

相关推荐
全栈前端老曹6 小时前
【包管理】read-pkg-up 快速上手教程 - 读取最近的 package.json 文件
前端·javascript·npm·node.js·json·nrm·package.json
2301_818732061 天前
安装了node,但是cmd找不到node和npm,idea项目也运行失败 已解决
前端·npm·node.js
Sapphire~1 天前
odoo-087 安装 npm (node ok npm not)
linux·运维·npm
Benny的老巢1 天前
【n8n工作流入门02】macOS安装n8n保姆级教程:Homebrew与npm两种方式详解
macos·npm·node.js·n8n·n8n工作流·homwbrew·n8n安装
2301_818732061 天前
下载nvm后,通过nvm无法下载node,有文件夹但是为空 全局cmd,查不到node和npm 已解决
前端·npm·node.js
稀饭522 天前
用changeset来管理你的npm包版本
前端·npm
就知道你是成心的2 天前
npm pack 一键构建npm离线包
npm
GuMoYu2 天前
npm link 测试本地依赖完整指南
前端·npm
爱写程序的小高3 天前
npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree
前端·npm·node.js
程序员的程3 天前
我做了一个前端股票行情 SDK:stock-sdk(浏览器和 Node 都能跑)
前端·npm·github