抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

七つの海

今日、海を見た。もう怖くない

一些关于 npm 中依赖和版本号的小结。知识盲区实在是太多了!

npm 中的依赖

npm 中的依赖主要有 dependenciesdevDependenciespeerDependencies

dependencies vs. devDependencies

顾名思义,前者是在生产环境和开发环境中都使用的包,而后者是仅在开发环境中使用的包。

比如 gulpwebpack 就是典型的只在开发环境中使用的包,无需打包到产品中。

开发环境和生产环境,可以通过 NODE_ENV=production|development 来指定。

对于 npm 而言,安装两种不同的依赖需要指定的完整标志分别是 --save--save-dev

peerDependencies

对于一般的 dependencies 的场合:如果我们有包 A 引用包 B 作为依赖,而我们在我们的项目中安装了包 A 作为依赖,那么实际上只有包 A 被安装到了我们的项目的 node_modules 中,包 B 实际上被安装到了包 A 安装目录下的 node_modules 中。这带来的结果是,虽然包 A 和包 B 都安装过了,我们可以使用 require 引用包 A,但是却不可以直接在我们的项目中引用包 B。

但是如果在包 A 中,包 B 是作为 peerDependencies 引入的,那么安装包 A 时,包 B 会被同样安装到我们项目的 node_modules 目录下;此时就可以直接在我们的项目中引用包 B。

综上所述,peerDepenedencies 的含义可以理解为对包管理器的要求:如果你安装了某个包,那么我建议你也安装我的 peerDependencies

npm2 在安装包时会强制安装包的 peerDependencies,不需要再宿主环境中指定对于这些包的依赖;而 npm3 不再强制安装这些依赖,而是在安装结束后检查本次安装是否正确;如果发现安装不正确则打印 WARN。不正确包括未安装和版本不匹配两种情况。

而如果出现了这种不正确的问题,只能手动解决:比如手动将这些依赖增加到 package.json 或者修改它们的版本使得这些依赖可以符合要求,然后 npm install --force…… 太蠢了!

1
 WARN  using --force I sure hope you know what you are doing

npm 版本号规则

整体来说,npm 的版本号的格式是 <major>.<minor>.<patch> 的形式。

版本号匹配规则

出现在 package.json 中的各种依赖包的版本实际上是匹配规则,又被称为 npm 语义化版本。一般来说有以下常用的匹配规则:

写法含义
version精确匹配某个特定版本
>version
<version
>=version
<=version
大于、小于、大于等于、小于等于某个特定版本;表示了一个范围
将两个规则连写也可以表示一个范围;如:>=version1 <version2
~version大致匹配某个版本。具体来说规则如下:
1)指定到 <patch>:形如 ~a.b.c;指大于当前指定的版本,但是小于下一个次版本号的所有版本
2)指定到 <minor>:形如 ~a.b;指固定主版本号和次版本号,补丁版本号任意
3)指定到 <major>:形如 ~a;指固定主版本号,次版本号和补丁任意
^version兼容某个版本,其含义是版本号中最左的非 0 版本号的右侧版本可以任意;
^a.b.c 实际上等价于 >=a.b.c<a+1.0.0 同时成立;
^0.a.b 则等价于 >=0.a.b<0.a+1.0 同时成立;
如果缺省了某个低权重的版本号,那么缺失的位置可以任意(此时类似 ~version
标识符 x标识符 x 的位置可以填入任何数字
标识符 *表示任意版本;等价于留空规则;严格的来说等价于 >=0.0.0
version1 - version2匹配了 [version1, version2] 双闭区间的版本
range1 || range2操作符 || 可以连接多个范围,表示匹配多个范围内的版本

由此可以看出 npm 语义化版本中有各种各样的模糊和范围,这为前端的工程化引入了一些问题:当某些包升级过程中没有遵循语义化版本,可能会导致每次打包生成代码都不同;所以我们需要特定项目依赖的包的版本号,为此各大包管理器都引入了 lockfile 的机制来锁定项目依赖的版本号。

开源的包一般都不包含 lockfile,其原因可能是为了避免特定过多具体的包导致引用较多开源包重复打包某个包的不同版本使得工程体积膨胀,故只能信任其依赖的包遵循语义化版本的要求,某个小版本/大版本的功能不发生过大变化而可以兼容。

包管理器的 installupdate

npm 为例,安装有 installupdate 两种命令;它们之间的区别主要体现在两个方面:

对于已安装的模糊版本

install 会忽略模糊版本,而 update 会更新模糊版本至最新版。

对于未安装的包,两者都会直接安装

对于 devDependencies

install 会安装/更新 devDependencies,除非指定 --production 标志。

update 会忽略 devDependencies,除非指定 --dev 标志。

参考

评论