阅读 142

「完全理解」各种 dependencies

主要探究不同 dependencies 配置对项目的影响

一、devDependencies


添加 devDependencies 依赖的方式

npm install <package-name> --save-dev

// 缩写
npm i <package-name> -D
复制代码

该配置项放置本地开发过程中需要使用到的编译、打包、测试、格式化模块等 ​

安装依赖的时候,不会安装依赖的 devDependencies ​

二、dependencies


添加 dependencies 依赖的方式

npm install <package-name>

// 缩写
npm i <package-name>
复制代码

项目中实际运行需要用到的代码,没有的话会出错 ​

1. 唯一依赖

例如:faker-project 项目依赖了 sdk-a

// faker-project
{
  "name": "faker-project",
  "dependencies": {
    "sdk-a": "1.0.0"
  }
}
复制代码

而 sdk-a 的 dependencies 中包含了 sdk-core

// sdk-a
{
  "name": "sdk-a",
  "version":"1.0.0",
  "dependencies": {
    "sdk-core": "0.0.1"
  }
}
复制代码

则 faker-project 执行 npm install 之后,目录结构如下

faker project
|
├── node_modules      
|  ├── sdk-a
|  └── sdk-core
|
└── package.json  
复制代码

所有依赖会平级设置 ​

2. 重复依赖

2.1 不兼容

假设 faker-project 项目又依赖了 sdk-b

// faker-project
{
  "name": "faker-project",
  "dependencies": {
    "sdk-a": "1.0.0",
+   "sdk-b": "1.0.0"
  }
}
复制代码

而 sdk-b 恰好也依赖了 sdk-core,但是 sdk-core 版本不同

// sdk-b
{
  "name": "sdk-b",
  "version":"1.0.0",
  "dependencies": {
    "sdk-core": "0.0.2"
  }
}
复制代码

则 faker-project 执行 npm install 之后,目录结构如下

  faker project
  |
  ├── node_modules      
  |  ├── sdk-a
+ |  ├── sdk-b
+ |  |  └── node_modules
+ |  |     └── sdk-core						// 0.0.2
+ |  |
  |  └── sdk-core									// 0.0.1
  |
  └── package.json  
复制代码

依赖会在自己的 node_modules 中单独安装一份,避免冲突 ​

2.2 兼容

如果 sdk-core 的版本能够兼容

// sdk-a
{
  "name": "sdk-a",
  "version":"1.0.1",
  "dependencies": {
-   "sdk-core": "0.0.1"
+   "sdk-core": "^0.0.1"
  }
}
复制代码

则两者会共用一份依赖(如果先安装一个,再安装另一个。最后目录结构也会和不兼容的情况表现一致)

  faker project
  |
  ├── node_modules      
  |  ├── sdk-a
  |  ├── sdk-b
  |  └── sdk-core									// 0.0.2
  |
  └── package.json  
复制代码

3. 历史

在 npm 3 之前,node_modules 不会平级设置。依赖的依赖都会直接嵌套 image.png

三、peerDependencies


一理解为「同级依赖」 ​

用在发布的代码库当中,表示需要宿主环境提供该配置下的模块依赖,与宿主环境息息相关。npm(3.x 版本之后,7.x 之前)、yarn 不会自动安装该配置下的依赖模块,会告警提示。 ​

时间节点: npm 3 - 2015 年 5 月 npm 5 - 2017 年 5 月 yarn 1 - 2017 年 9 月 yarn 2 - 2020 年 1 月 npm 7 - 2021 年 3 月

**PS:**为了便于理解,下文中:

  • 新版:npm 7 及其以上的版本,会自动安装 peerDependencies
  • 旧版:npm 3.x ~ 6.x 和 yarn,不会自动安装 peerDependencies

1. 唯一依赖

// sdk-a
{
  "name": "sdk-a",
  "version":"1.0.2",
  "peerDependencies": {
    "sdk-core": "^0.0.1"
  }
}
复制代码

1.1 旧版

不会自动安装该配置下的依赖模块,会告警提示。需要用户自行安装

warning " > sdk-a@1.0.3" has unmet peer dependency "sdk-core@^0.0.1".
复制代码
  faker project
  |
  ├── node_modules      
  |  └── sdk-a
  |
  └── package.json
复制代码

1.2 新版

会自动安装,并始终只存在一个。直接位于 node_modules 之下 ​

1.3 区别

不管自动还是手动,最后的结果都是。peerDependencies 中的依赖都会被直接安装在 node_modules 之下 ​

最大区别是,旧版通过自己的手动安装。会体现在项目的 dependencies 中,而新版不会

// faker-project
{
  "name": "faker-project",
  "dependencies": {
    "sdk-a": "1.0.3",
    "sdk-core": "^0.0.1"					// 最大区别
  }
}
复制代码

2. 重复依赖

2.1 不兼容

2.1.1 旧版

只会在安装时提示不同版本的 peer dependency 信息

warning " > sdk-a@1.0.4" has unmet peer dependency "sdk-core@0.0.1".
warning " > sdk-b@1.0.2" has unmet peer dependency "sdk-core@0.0.2".
复制代码

2.1.2 新版

对于版本不兼容的依赖,安装时候会直接报错 ​

可以通过 --legacy-peer-deps 让 peerDependencies 不自动安装 ​

2.1.3 如何处理不兼容的 peerDependencies

目前没有让不兼容的 peerDependencies 各自生效的方法。所以真碰到了这种情况,一般处理方法是修改其中一个带有 peerDependencies 依赖的包版本。让其依赖另一版本的 peerDependencies 和其余版本的兼容

2.2 兼容

// sdk-b
{
  "name": "sdk-b",
  "version":"1.0.3",
  "peerDependencies": {
    "sdk-core": "^0.0.1"
  }
}
复制代码
// faker-project
{
  "name": "faker-project",
  "dependencies": {
    "sdk-a": "1.0.4",
    "sdk-b": "1.0.3"
  }
}
复制代码

多个项目存在兼容 peerDependencies 的情况

2.2.1 旧版

不自动安装,仅提示 ​

2.2.2 新版

自动安装,和唯一依赖行为一样

2.3 特殊情况

如果 sdk-core,同时存在 peerDependencies 和 dependencies 时 ​

2.3.1 旧版

不会被安装,等同于只有 peerDependencies ​

2.3.2 新版

会自动安装,等同于只有 dependencies ​

两种情况均不判断版本兼容

2.4 嵌套依赖

还有更极端的情况,比如 dependencies 里如果有 peerDependencies。详情见下面的文章

四、总结


  • 运行时用不到的依赖,放在 devDependencies 中
  • 插件类的依赖(比如 sdk-core 是一个依赖,在此基础上开发的依赖 sdk-a、sdk-b)才使用 peerDependencies
  • 要求运行时时单例的依赖(vue、react 等同时只能存在一个实例)使用 peerDependencies
  • 其余情况都使用 dependencies

五、其他 dependencies


其他比较少见的 dependencies ​

1. optionalDependencies

optionalDependencies 可选依赖,如果有一些依赖包即使安装失败,项目仍然能够运行或者希望 npm 继续运行,就可以使用 optionalDependencies。另外 optionalDependencies 会覆盖 dependencies 中的同名依赖包,所以不要在两个地方都写 ​

举个栗子,可选依赖包就像程序的插件一样,如果存在就执行存在的逻辑,不存在就执行另一个逻辑。

try {
  var foo = require('foo')
  var fooVersion = require('foo/package.json').version
} catch (er) {
  foo = null
}
if ( notGoodFooVersion(fooVersion) ) {
  foo = null
}

// .. then later in your program ..

if (foo) {
  foo.doFooThings()
}
复制代码

2. bundledDependencies / bundleDependencies

打包依赖,bundledDependencies 是一个包含依赖包名的数组对象,在发布时会将这个对象中的包打包到最终的发布包里。如:​

{
  "name": "fe-weekly",
  "description": "ELSE 周刊",
  "version": "1.0.0",
  "main": "index.js",
  "devDependencies": {
    "fw2": "^0.3.2",
    "grunt": "^1.0.1",
    "webpack": "^3.6.0"
  },
  "dependencies": {
    "gulp": "^3.9.1",
    "hello-else": "^1.0.0"
  },
  "bundledDependencies": [
    "fw2",
    "hello-else"
  ]
}
复制代码

执行打包命令 npm pack, 在生成的 fe-weekly-1.0.0.tgz 包中,将包含 fw2 和 hello-else。 但是值得注意的是,这两个包必须先在 devDependencies 或 dependencies 声明过,否则打包会报错。

参考

你需要知道的几类 npm 依赖包管理 - zhuanlan.zhihu.com/p/29855253

文章分类
前端
文章标签