npm是一个Javascript项目的包管理工具。由于与Node.js合作紧密(npm和Node.js是一人创建的),目前基本没有竞争对手。包管理工具需要解决的主要问题是依赖包的安装。在Javascript项目中,包依赖项由package.json给出。本文档将介绍package.json中描述的依赖信息。依赖管理在package.json中,有如下与依赖包相关的描述:dependencies项目中使用的包,但不包括测试中使用的包devDependencies主要用于测试包,包括一些代码编译包,如将咖啡脚本编译为javascript。也就是说,当只使用项目时(没有测试等),不需要安装的包可以放在devDependenciespeerDependencies中,如果在更改项目时需要指定一些协作包的版本,使用peerDependencies.这里用的是协作,不是依赖,这是我个人的理解。peerDependencies不是必须安装的包,但是如果安装了,必须满足要求。比如react-dom的package.json有这样的描述:"peerDependencies":{"react":"^15.6.1"},peerDependencies至少打消了一些顾虑,比如react生态中用到的一些package是正在升级会不会定时不匹配?optionalDependencies如果有一些依赖包即使安装失败,项目仍然可以运行或者你想让npm继续运行,你可以使用optionalDependencies。另外optionalDependencies会覆盖dependencies中同名的依赖,所以不要两处都写。bundledDependencies/bundleDependencies如果你希望在发布包的时候,一些依赖的包出现在最终的包中,可以把包名放在bundledDependencies中,bundledDependencies的值是一个字符串数组,比如:"name":"awesome-web-framework","version":"1.0.0","bundledDependencies":['renderized','super-streams']npmpack会将渲染和超级流放入生成的包awesome-web-framework-1.0.0.tgz,当npminstallawesome-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"}键是名称project,Value可以有多个版本,semver后面一个tarballurl一个giturl本地路径关于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.0andNode.js在加载依赖包时会处理这个问题。这也是文章首先提到npm与Node.js紧密合作的原因。使用依赖树来管理包带来了明显的好处,而无需处理依赖冲突。这个问题在早期的Java项目中比较常见,在使用了Maven和Gradle之后,情况有所缓解,但是只能缓解同一个包的多个版本放入发布包的情况,仍然无法解决依赖冲突的根本问题。依赖树也有一些缺点:代码量增加。由于同一个包无法共享(在npmv3中尝试共享同一个版本的库,但还是比较弱),导致最终打包时最终包中出现同一个库代码的多个版本.潜在的运行时冲突。以上面的例子为例,可能会出现两个版本的mod-b无法同时运行的情况,这只能在运行或者测试的时候才能发现。希望以上的介绍可以帮助大家更好的理解npm的依赖管理。
