本文章转载自:
作者:wonyun
原文地址:探讨npm依赖管理之peerDependencies
想必前端同学对npm的devDependencies和dependencies都比较熟悉,但是对peerDependencies可能就有点陌生,尤其是没有写过npm包插件的同学,比如之前使用grunt自动化工具的相关插件(如grunt-contrib-jasmine等)或者目前基于某个框架的ui组件库等等,这些都是需要对peerDependencies有一定了解的。下面我们就来说说peerDependencies。
假设我们当前的项目是MyProject,项目中有一些依赖,其中一个依赖包PackageA它的package.json文件指定了对PackageB的依赖:
{
"dependencies": {
"PackageB": "1.0.0"
}
}
如果我们在我们的MyProject项目中执行npm install PackageA, 我们会发现我们项目的目录结构会是如下形式:
MyProject
|- node_modules
|- PackageA
|- node_modules
|- PackageB
那么在我们的项目中,我们能通过下面语句引入"PackageA":
var packageA = require('PackageA')
但是,如果你想在项目中直接引用PackageB:
var packageA = require('PackageA')
var packageB = require('PackageB')
这是不行的,即使PackageB被安装过;因为Node只会在“MyProject/node_modules”目录下查找PackageB,它不会在进入PackageA模块下的node_modules下查找。
所以,为了解决这个问题,在MyProject项目package.json中我们必须直接声明对PackageB的依赖并安装。
但是,有时我们不用在当前项目中声明对PackageB的依赖就可以直接引用,尤其是,PackageA是一个类似于grunt的插件,例如grunt-contrib-jshint。
为什么在项目中不用声明就可以直接使用呢?这就不得不说说peerDependencies的作用了。
为了解决这种问题:如果你安装我,那么你最好也安装X,Y和Z.
于是peerDependencies就被引入了。例如上面PackageA的package.json文件如果是下面这样:
{
"peerDependencies": {
"PackageB": "1.0.0"
}
}
那么,它会告诉npm:如果某个package把我列为依赖的话,那么那个package也必需应该有对PackageB的依赖。
也就是说,如果你npm install PackageA,你将会得到下面的如下的目录结构:
MyProject
|- node_modules
|- PackageA
|- PackageB
你可能注意到:
在npm2中,即使当前项目MyProject中没有直接依赖PackageB,该PackageB包依然会安装到当前项目的node_modules文件夹中。
这就是因为MyProject/node_modules中某些包在他的deerDependencies中指定了PackageB
此时,下面的代码现在可以正常工作了,因为两个包在"MyProject/node_modules"中被安装了:
var packageA = require('PackageA')
var packageB = require('PackageB')
总结一句话,peerDependencies的具体作用:
peerDependencies的目的是提示宿主环境(项目)去安装满足插件peerDependencies所指定依赖的包,然后在插件import或者require所依赖的包的时候,永远都是引用宿主环境(项目)统一安装的npm包,最终解决插件与所依赖包不一致的问题。
举个例子,就拿目前基于react的ui组件库[email protected]来说,因该ui组件库只是提供一套react组件库,它要求宿主环境需要安装指定的react版本。具体可以看它package.json中的配置:
"peerDependencies": {
"react": ">=16.0.0",
"react-dom": ">=16.0.0"
}
它要求宿主环境安装react@>=16.0.0和react-dom@>=16.0.0的版本,而在每个antd组件的定义文件顶部:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
组件中引入的react和react-dom包其实都是宿主环境(MyProject/node_modules)提供的依赖包。
正如上一节谈论的,在npm2中,PackageA包中peerDependencies所指定的依赖会随着npm install PackageA一起被强制安装,所以不需要在宿主环境的package.json文件中指定对PackageA中peerDependencies内容的依赖。
但是在npm3中,peerDependencies的表现与npm2不同:
npm3中不会再要求peerDependencies所指定的依赖包被强制安装,相反npm3会在安装结束后检查本次安装是否正确,如果不正确会给用户打印警告提示。
举个例子,我当前电脑的环境是node: v11.2.0,npm:v6.10.1,webpack是v4.41.2,然后我新建一个空白项目,执行npm install sass-loader时,他会给我一个这样的提示:
此时安装好sass-loader后,他给了我们3个warning,提示说我们需要安装node-sass@4、[email protected]、[email protected]这几个包,也就是说,如果我不安装这几个包,然后直接在webpack.config.js中写sass的相关配置后执行打包,其实是会报错的,说明sass-loader在他的peerDependencies中提示了我们,要想正常运行sass-loader,必须安装他指定的这几个包,同时他不会自动帮我们安装且写到package.json,这个过程需要我们手动地去操作,我们再看下sass-loader的package.json确认下:
"peerDependencies": {
"webpack": "^4.36.0",
"node-sass": "^4.0.0",
"sass": "^1.3.0",
"fibers": ">= 3.1.0"
},
验证后跟我们设想的一样,除了已经安装过的webpack之外,其他没安装的都报出了warning提示,此时我们按照他给的提示手动的去安装一下指定版本的node-sass、sass、fibers,就可以使webpack打包正常进行了
另外,在npm3及以上版本的项目中,可能存在一个问题就是你所依赖的一个package包更新了它peerDependencies的版本,那么你可能也需要在项目的package.json文件中手动更新到正确的版本。否则会出现类似下图所示的警告信息:
Peer Dependencies
package.json
Dealing with the deprecation of peerDependencies in NPM 3