依赖管理

node自带依赖管理工具NPM

依赖版本

package.json

项目的依赖保存在根目录的package.json中。

  • dependencies

项目运行所需依赖

  • devDependencies

仅在项目开发时需要的依赖

  • peerDependencies

非项目直接依赖,但项目需要它们配合来运行。表示安装本项目时最好也安装了它们,比如工具类项目的宿主

版本号

依赖的版本用"x.y.z"的格式来表示。

  • 版本号遵循semver规则。x为major版本,表示不兼容的升级;y为minor版本,表示一些向下兼容的新增功能;z为patch版本,表示一些不影响功能的修复和优化。版本号严格递增。

  • "x.y.z"表示需要的版本号是精确的"x.y.z"。

  • "^x.y.z"表示允许y和z为最新。npm install时默认保存的是此策略。

  • "~x.y.z"表示允许z为最新。

  • ">=x.y.z"表示版本大于等于"x.y.z"即可。

  • 多个"与关系"的版本规则可以用空格组合,比如">1.2.3 <1.3.10";"或关系"的版本规则用"||"隔开。

版本锁定

因为版本策略允许使用一个范围内的版本而非精确版本,所以不同环境下的依赖可能会有所不同,也会因此带来一些问题。

NPM5之后,额外使用了package-lock.json来指明依赖的具体版本,该文件会在npm install时自动生成。

在项目里有package-lock.json的情况下,执行npm install分两种情况:

  • package.json里的依赖和package-lock.json里的都对得上,那么直接使用package-lock.json里的精确版本来安装依赖。

  • package.json里的某些依赖有更新,并且和package-lock.json里的版本对不上了,那么会根据package.json指定的这个版本策略来安装依赖,并且将当前安装的具体版本自动更新到package-lock.json的对应字段中。

另外,package-lock.json里的dependencies树结构是和node_modules里的文件结构一一对应的。

冗余问题

对于项目里重复的依赖,直接依赖是放在node_modules顶层的,所以多个直接依赖指向的都是同一份依赖。

间接依赖默认被安装到直接依赖各自的子目录下,但此时会有冗余问题。比如直接依赖A和直接依赖B,它们都依赖了X,X即为项目的间接依赖。如果在A和B中各自安装一份X,则多了一份冗余的X。

对于间接依赖的冗余,NPM采取了优化策略。如果发现这种重复的依赖X,就把这个X提取到顶层,因为寻找依赖时自动向上递归查找,所以A和B可在顶层拿到这个X,这样就不会有多余的X。

但此策略下还是会有冗余问题。比如上面的情况中,A和B依赖的X为版本X1,又有一个C,它也依赖了X,但版本是与X1不兼容的X2,那么C就无法使用顶层的X1,此时C会在自己的目录下安装X2。如果后面又有一个D也依赖了X2,那么D也会在自己的目录下安装一份X2。此时项目里还是会有一份冗余的X2。

相比go的依赖管理,go mod安装的依赖是目录名带了版本号平铺开在顶层,所以不管怎样同一版本的依赖项目里只会有一份。似乎这样的设计更加合理。