依赖包版本号
npm
采用了semver
规范作为依赖版本管理方案。
按照semver
的约定,一个npm
依赖包的版本格式一般为:主版本号.次版本号.修订号(x.y.z
),每个号的含义是:
-
主版本号(也叫大版本,
major version
)大版本的改动很可能是一次颠覆性的改动,也就意味着可能存在与低版本不兼容的
API
或者用法,(比如vue 2 -> 3
)。 -
次版本号(也叫小版本,
minor version
)小版本的改动应当兼容同一个大版本内的
API
和用法,因此应该让开发者无感。所以我们通常只说大版本号,很少会精确到小版本号。如果大版本号是 0 的话,表示软件处于开发初始阶段,一切都可能随时被改变,可能每个小版本之间也会存在不兼容性。所以在选择依赖时,尽量避开大版本号是 0 的包。
-
修订号(也叫补丁,
patch
)一般用于修复
bug
或者很细微的变更,也需要保持向前兼容。
常见的几个版本格式如下:
-
“1.2.3”
表示精确版本号。任何其他版本号都不匹配。在一些比较重要的线上项目中,建议使用这种方式锁定版本。
-
“^1.2.3”
表示兼容补丁和小版本更新的版本号。官方的定义是“能够兼容除了最左侧的非 0 版本号之外的其他变化”(Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple)。这句话很拗口,举几个例子大家就明白了:
"^1.2.3" 等价于 ">= 1.2.3 < 2.0.0"。即只要最左侧的 "1" 不变,其他都可以改变。所以 "1.2.4", "1.3.0" 都可以兼容。
"^0.2.3" 等价于 ">= 0.2.3 < 0.3.0"。因为最左侧的是 "0",那么只要第二位 "2" 不变,其他的都兼容,比如 "0.2.4" 和 "0.2.99"。
"^0.0.3" 等价于 ">= 0.0.3 < 0.0.4"。大版本号和小版本号都为 "0" ,所以也就等价于精确的 "0.0.3"。
-
从这几个例子可以看出,
^
是一个兼具更新和安全的写法,但是对于大版本号为 1 和 0 的版本还是会有不同的处理机制的。 -
“~1.2.3”
表示只兼容补丁更新的版本号。关于
~
的定义分为两部分:如果列出了小版本号(第二位),则只兼容补丁(第三位)的修改;如果没有列出小版本号,则兼容第二和第三位的修改。我们分两种情况理解一下这个定义:
"~1.2.3" 列出了小版本号 "2",因此只兼容第三位的修改,等价于 ">= 1.2.3 < 1.3.0"。
"~1.2" 也列出了小版本号 "2",因此和上面一样兼容第三位的修改,等价于 ">= 1.2.0 < 1.3.0"。
"~1" 没有列出小版本号,可以兼容第二第三位的修改,因此等价于 ">= 1.0.0 < 2.0.0"
-
从这几个例子可以看出,
~
是一个比^
更加谨慎安全的写法,而且~
并不对大版本号 0 或者 1 区别对待,所以 “~0.2.3” 与 “~1.2.3” 是相同的算法。当首位是 0 并且列出了第二位的时候,两者是等价的,例如 “~0.2.3” 和 “^0.2.3”。 -
“1.x” 、“1.X”、1.“、“1”、”"
表示使用通配符的版本号。x、X、* 和 (空) 的含义相同,都表示可以匹配任何内容。具体来说:
"*" 、"x" 或者 (空) 表示可以匹配任何版本。
"1.x", "1.*" 和 "1" 表示匹配主版本号为 "1" 的所有版本,因此等价于 ">= 1.0.0 < 2.0.0"。
"1.2.x", "1.2.*" 和 "1.2" 表示匹配版本号以 "1.2" 开头的所有版本,因此等价于 ">= 1.2.0 < 1.3.0"。
- “1.2.3-alpha.1”、“1.2.3-beta.1”、“1.2.3-rc.1”
带预发布关键词的版本号。先说说几个预发布关键词的定义:
alpha(α):预览版,或者叫内部测试版;一般不向外部发布,会有很多bug;一般只有测试人员使用。
beta(β):测试版,或者叫公开测试版;这个阶段的版本会一直加入新的功能;在alpha版之后推出。
rc(release candidate):最终测试版本;可能成为最终产品的候选版本,如果未出现问题则可发布成为正式版本。
以包开发者的角度来考虑这个问题:假设当前线上版本是 “1.2.3”,如果我作了一些改动需要发布版本 “1.2.4”,但我不想直接上线(因为使用 “~1.2.3” 或者 “^1.2.3” 的用户都会直接静默更新),这就需要使用预发布功能。因此我可能会发布 “1.2.4-alpha.1” 或者 “1.2.4-beta.1” 等等。
">1.2.4-alpha.1"表示接受 "1.2.4-alpha" 版本下所有大于 1 的预发布版本。因此 "1.2.4-alpha.7" 是符合要求的,但 "1.2.4-beta.1" 和 "1.2.5-alpha.2" 都不符合。此外如果是正式版本(不带预发布关键词),只要版本号符合要求即可,不检查预发布版本号,例如 "1.2.5", "1.3.0" 都是认可的。
"~1.2.4-alpha.1" 表示 ">=1.2.4-alpha.1 < 1.3.0"。这样 "1.2.5", "1.2.4-alpha.2" 都符合条件,而 "1.2.5-alpha.1", "1.3.0" 不符合。
"^1.2.4-alpha.1" 表示 ">=1.2.4-alpha.1 < 2.0.0"。这样 "1.2.5", "1.2.4-alpha.2", "1.3.0" 都符合条件,而 "1.2.5-alpha.1", "2.0.0" 不符合。
版本号还有更多的写法,例如范围(a - b
),大于等于号(>=
),小于等于号(<=
),或(||
)等等,因为用的不多,这里不再展开。详细的文档可以参见语义化版本(semver)。它同时也是一个 npm
包,可以用来比较两个版本号的大小,以及是否符合要求等。
依赖包版本管理
npm 2.x/3.x
已成为过去式,在npm 5.x
以上环境下(版本最好在5.6
以上,因为在5.0 ~ 5.6
中间对package-lock.json
的处理逻辑更新过几个版本,5.6
以上才开始稳定),管理项目中的依赖包版本你应该知道(以^
版本为例,其他类型版本参照即可):
- 在大版本相同的前提下,如果一个模块在
package.json
中的小版本要大于package-lock.json
中的小版本,则在执行npm install
时,会将该模块更新到大版本下的最新的版本,并将版本号更新至package-lock.json
。如果小于,则被package-lock.json
中的版本锁定。
// package-lock.json 中原版本
"clipboard": {
"version": "1.5.10",
},
"vue": {
"version": "2.6.10",
}
// package.json 中修改版本
"dependencies": {
"clipboard": "^1.5.12",
"vue": "^2.5.6"
...
}
// 执行完 npm install 后,package-lock.json 中
"clipboard": {
"version": "1.7.1", // 更新到大版本下的最新版本
},
"vue": {
"version": "2.6.10", // 版本没发生改变
}
- 如果一个模块在
package.json
和package-lock.json
中的大版本不相同,则在执行npm install
时,都将根据package.json
中大版本下的最新版本进行更新,并将版本号更新至package-lock.json
。
// package-lock.json 中原版本
"clipboard": {
"version": "2.0.4",
}
// package.json 中修改版本
"dependencies": {
"clipboard": "^1.6.1",
}
// 执行完npm install后,package-lock.json 中
//
"clipboard": {
"version": "1.7.1", // 更新到大版本下的最新版本
}
-
如果一个模块在
package.json
中有记录,而在package-lock.json
中无记录,执行npm install
后,则会在package-lock.json
生成该模块的详细记录。同理,一个模块在package.json
中无记录,而在package-lock.json
中有记录,执行npm install
后,则会在package-lock.json
删除该模块的详细记录。 -
如果要更新某个模块大版本下的最新版本(升级小版本号),请执行如下命令:
npm update packageName
- 如果要更新到指定版本号(升级大版本号),请执行如下命令:
npm install packageName@x.x.x
- 卸载某个模块,请执行如下命令:
npm uninstall packageName
- 安装模块的确切版本:
npm install packageName -D/S --save-exact # 安装的版本号将会是精准的,版本号前面不会出现^~字符
通过上述的命令来管理依赖包,package.json
和package-lock.json
中的版本号都将会随之更新。
我们在升级/卸载依赖包的时候,尽量通过命令来实现,避免手动修改
package.json
中的版本号,尤其不要手动修改package-lock.json
。