npm作为Javascript项目的包管理工具,因为其与Node.js的紧密配合(npm和Node.js出自一人之手),目前已经基本没有竞争对手。javascript
包管理工具要解决的主要问题就是依赖包的安装,在Javascript项目中,包的依赖关系是由package.json给出的,这篇文件将介绍package.json中描述的依赖信息。java
在package.json中,有以下几项涉及到依赖包的描述:node
dependenciesreact
项目中使用到的包,但不包括测试所使用的包
devDependenciesgit
主要是在测试时使用的包,也包括一些代码编译的包,好比将coffee-script编译为javascript。也就是说在仅仅使用该项目的时候(而不进行测试等环节),不须要安装的包能够放在devDependencies中github
peerDependenciesweb
若是改项目须要指明一些有协做关系的包的版本时,使用peerDependencies。这里使用了协做,而不是依赖,是我我的的理解。peerDependencies并非必须安装的包,但若是一旦要安装,就要符合要求。好比react-dom的package.json中有以下的描述:npm
"peerDependencies": { "react": "^15.6.1" },
peerDependencies至少打消了一些顾虑,好比react生态中用到的一些包在升级的时候会不会不匹配?json
optionalDependencies数组
若是有一些依赖包即便安装失败,项目仍然可以运行或者但愿npm继续运行,就可使用optionalDependencies。另外optionalDependencies会覆盖dependencies中的同名依赖包,因此不要在两个地方都写。
bundledDependencies/bundleDependencies
若是在打包发布的使用但愿一些依赖包也出如今最终的包里,那么能够将包的名字放在bundledDependencies,bundledDependencies的值是一个字符串数组,如:
"name": "awesome-web-framework", "version": "1.0.0", "bundledDependencies": [ 'renderized', 'super-streams' ]
npm pack
会将renderized
和super-streams
放入生成的包awesome-web-framework-1.0.0.tgz中,而且在npm install awesome-web-framework-1.0.0.tgz
时,renderized
和super-streams
也会被一同安装。
这些项的内容形式都是同样的(除了bundledDependencies),只是做用不一样,如:
"dependencies": { "base62": "~0.1.1", "commoner": "~0.7.0", "esprima": "https://github.com/facebook/esprima/tarball/ca28795124d45968e62a7b4b336d23a053ac3a84", "recast": "~0.4.8", "source-map": "~0.1.22" }
key就是项目的名词,而value能够有多种形式
version,遵循semver
一个tarball的url
一个git url
本地路径
关于semver会在另外一篇文章中介绍(先挖个坑)。
不一样于不少语言的依赖管理,npm使用的是依赖树。也就是说每一个依赖包会有一套本身指定的(在package.json中说明的)依赖包,而不会和其余包共享。
例如,mod-a依赖mod-b@1.0.0,mod-c依赖mod-b@2.0.0,而如今有一个项目依赖mod-a和mod-c,那么最终安装的依赖包以下:
├─┬ node_modules │ ├─┬ mod-a │ │ ├── mod-b@1.0.0 │ ├─┬ mod-c │ │ ├── mod-b@2.0.0
而Node.js在加载依赖包的时候会处理这个问题。之因此文章最开始说npm和Node.js的紧密合做也是这个缘由。
使用依赖树来管理包带来了一个明显的好处,不用处理依赖冲突的问题。这个问题在早期的Java项目中比较常见,而在使用了Maven和Gradle以后,状况有所缓解,但只能减轻同一个包多个版本被放入发布的包中这种状况,仍然没法解决依赖冲突这个根本问题。
依赖树也有一些缺点:
代码量增长。因为不能共享相同的包(在npm v3中,尝试着共享相同版本的库,但仍是比较弱),致使最终打包的时候有同一个库的多个版本的代码出如今最终包中。
潜在的运行时冲突。以上面的例子为例,可能有些时候,mod-b的两个版本没法同时运行,而这只有在运行或者测试的时候才能被发现。
但愿以上的介绍可以帮助你更好的理解npm的依赖管理。