版本号,说白了就是我们为项目的每个不同版本起的标识号,其被广泛运用于开发的各种场景:NPM(Node Package Manager) 的版本定义、对 NPM 包的特定版本的依赖指定、Git 的 daily 版本号分支等等。面对如此多的场景,版本号的命名却存在很大问题。例如:
- 开始写一个新项目 / 模块时,不管三七二十一,都从
0.0.1
起版本,直到项目不再维护时,版本还停留在0.0.48
,前两位永远都是0
。 - API 变化巨大,而版本号雷打不动一步一个脚印。一个二方包从
0.0.8
升级到0.0.9
就引起了整个项目的崩溃。 - 依赖二方 / 三方包时,不知道该依赖哪个版本,有时随便指定了一个,有时则直接依赖了
*
。
版本号的命名
根据国际主流的惯例,我们使用「语义化版本(Semantic Versioning)」的命名方式,有时简称 SemVer。语义化版本号(以下简称「版本号」)的格式是:<major>.<minor>.<patch>
。即使用三位非负整数,以点号.
连接。
- 如:
1.4.15
、6.2.0
。
每一位版本号的含义
<major>
即主版本号,俗称大版本升级。改动到主版本号时,标志着 API 发生了巨大变化,包括但不限于新增特性、修改机制、删除功能, 一般不兼容上一个主版本号。<minor>
即次版本号,俗称小版本升级。当我们进行常规的新增或修改功能时,改动次版本号,但是必须是向前兼容的。这也意味着我们不能直接删除某个功能。如若必要,我们可以在修改日志中标记某项功能为「即将删除(Deprecated)」,然后在下一个大版本中将其彻底删除。<patch>
即修订号,俗称 bug 修复。顾名思义,如果仅仅为了修复或调整一些小问题,我们就只改动修订号。
所以,当我们明确了每一位的含义和作用后,就不会陷入「每次只改最末位」的尴尬中了。那如何判断一个修改应该是改动修订号还是次版本号呢?视情况而定。比如对于「修改了 app 图标」这件事来说,如果只是调整了图标的间距位置,那么可以认作问题修复;如果把整个图标换了,配上了不同的标语,那么这应该是一次功能改动。
注意事项
- 版本号前不要加
v
。 - 不要在数字前补
0
。错误示例:01.12.03
。 - 每一位版本号按照
+1
的速度递增,不要在版本号之间跳跃。 - 主版本号停留在
0
的版本号,即0.x.x
应当视作还在内部开发阶段的代码。如果代码有公共 API,此时不宜对外公开。 1.0.0
的版本号用于界定公共 API 的形成。- 当次版本号递增时,修订号归零;当主版本号递增时,次版本号、修订号归零。
- 进行新的开发时,版本号从
0.1.0
开始。 - 如果不小心把一个不兼容的改版当成了次版本号发行,应当发行一个新的次版本号来更正这个问题并且恢复向下兼容。注意不能去修改已发行的版本。
一个典型的版本号发展示例
0.1.0
0.1.1
0.1.2
0.2.0
1.0.0
1.1.0
1.1.1
……
快速修改版本号
如果一个包发布在 NPM / TNPM 中,可以快速修改其版本号。会自动触发一个 Git 提交。
# 递增一个修订号
npm version patch
# 递增一个次版本号
npm version minor
# 递增一个主版本号
npm version major
预发版本号
在常规的版本号命名之上还有一个特殊类别,叫做预发版本号(Prerelease Version)。它表示当前版本是一个不稳定的版本,使用它时需要注意风险。
预发版本号的格式是<major>.<minor>.<patch>-<tag>
,即前半部分和常规版本号相同,然后跟上连接符-
,后面再跟上字母数字点号连接符([0-9A-Za-z-.]
)。
一个典型的预发版本号形如1.0.0-beta.1
。建议使用这种<major>.<minor>.<patch>-<stage>.<num>
的形式。其中<stage>
一般选用:alpha
、beta
、rc
。
预发版本号是常规版本号的附属,因此在版本的大小比较上,仍然先比较常规版本号部分;对于预发标记部分的比较,则是根据 ASCII 字母表中的顺序来进行。
一个典型的预发版本号发展示例
0.9.0
1.0.0-alpha.1
1.0.0-alpha.2
1.0.0-beta.1
1.0.0-rc.1
1.0.0
1.0.1
……
依赖的版本号标记法
我们广泛使用的 NPM 本身也遵从 SemVer 版本号命名,除了包版本本身的定义之外,最重要的是对三方包依赖的版本号的定义,不当的写法将导致一系列潜在的问题。
指定可用的版本号范围
在 NPM 包的 deps 系列字段中,经常出现形如~1.0.4
、^2.1.1
这样的标记法,这种标记法标记的是「版本号范围(Version Range)」,它表示依赖的三方包其版本号只要落在定义版本号范围内,即算合法。另外,当运行 npm update 时,依赖的包将升级到版本号范围支持的最高版本。
版本号范围的标记符号有很多种,诸如比较符号>=
、<
等;连接符-
;通配符x
、*
;模糊符^
、~
。具体的用法可参考「NPM 官方文档」,这里仅给出常用的标记方式。
含义 | 最简 | 通配符 | 模糊符 | 版本号范围 |
---|---|---|---|---|
仅跟进修复版本 | 1.0 |
1.0.x |
~1.0.4 |
>=1.0.4 <1.1.0 |
跟进每个小版本更新 | 1 |
1.x 、1.x.x |
^1.0.4 |
>=1.0.4 <2.0.0 |
始终升级到最新版 | * |
* |
* |
>=0.0.0 |
我们建议在写法上采用 「使用通配符的写法」,并且一般情况下 「跟进每个小版本更新」,但不「始终升级到最新版」,即书写为1.x
。由于<major>
位版本是不向下兼容的,所以在大版本的控制上,仍然采用人为干预以保证安全。
不同的 deps 字段
NPM 包中的依赖有几种形式的字段:dependencies、devDependencies、peerDependencies。以下简要介绍下各字段的不同含义,以及使用场景。
字段 | 含义 | 依赖被安装的时机 | 使用场景 |
---|---|---|---|
dependencies | 运行时依赖,包的调用者需要使用到的依赖 | 执行 npm install 后会把当前包的dependencies 字段中的所有依赖项安装到./node_modules 目录。执行 npm install xxx 后会把 xxx 安装到./node_modules 下,同时会安装 xxx 的 dependencies 字段依赖项到./node_modules/xxx/node_modules 目录。 执行 npm install xxx –save 后会额外把 xxx 作为依赖存到当前包的 dependencies 字段中。 |
所有程序运行需要用到的依赖代码,如 lodash 等。 |
devDependencies | 开发时依赖,包的开发维护者需要使用到的依赖 | 执行 npm install 后也会把当前包的devDependencies 字段中的所有依赖项安装到./node_modules 目录。执行 npm install xxx 后会把 xxx 安装到./node_modules 下,但不会安装 xxx 的 devDependencies 字段依赖项。执行 npm install xxx –save-dev 后会额外把 xxx 作为开发时依赖存到当前包的 devDependencies 字段中。 |
一般是一些开发调试的辅助工具,如测试工具 mocha、构建工具 gulp 等。 |
peerDependencies | 略 | 略 | 仅在特定场景下有用,默认不使用此字段。 |
转载声明:本文转自「汇智网」,论版本号的正确打开方式,略有修改。
语义化版本(SemVer)的范围
在使用 Node.js 和 Bower 的时候,其中的 package.json
和 bower.json
都会有 dependencies
、 devDependencies
项,对于此前端开 发的童鞋应该会比较熟悉。这里面是运行时和开发时依赖库,其中依赖库对应的版本号有很多种写法,诸如:” ~1.0.2
“, “ ^1.0
“, “ >=1.0.2
“等,其实可以猜测这种写法代表了某种范围,至于具体含义本文给大家做详细解释。
语义化版本格式
我们首先简单了解一下语义化版本版本号,标准的版本格式为: X.Y.Z
,其中:
X
:主版本号,当我们做了不兼容或者颠覆性的更新,修改此版本号。
Y
:此版本号,当我们做了向下兼容的功能性修改,修改此版本号。
Z
:修订号,当我们做了向下兼容的问题修正,修改此版本号。
其中 X
、 Y
和 Z
必须为非负整数,禁止数字前补零,每个数值都是递增的。
语义化版本范围
版本范围是一组满足指定范围的比较器,一个比较器是由操作符和版本号组成,下面是最原始的操作符:
<
小于;<=
小于等于;>
大于;>=
大于等于;=
等于;如果没有指定操作符,则默认为等于。
一个范围可由一个或者多个比较器组成,如果有多个,则由双竖线(||)连接。对于包含多个比较器,只要满足其一即可。比如:
- 范围
>=1.2.7 <1.3.0
,版本号1.2.7
,1.2.8
,1.2.99
满足条件,而1.2.6
,1.3.0
,1.1.0
确不满足。 - 范围
1.2.7
||>=1.2.9 <2.0.0
,版本号1.2.7
,1.2.9
,1.4.6
满足,而1.2.8
或者2.0.0
不满足。
关于测试版
Alpha
、Beta
、Gamma
与α
、β
、λ
谐音,是希腊字母前三个字母,用来表示软件开发过程中测试的三个阶段:
Alpha
:内测版,内部交流或者专业测试人员测试用;
Beta
:公测版,专业爱好者大规模测试用,存在一些缺陷,该版本也不适合一般用户安装;
Gamma
:比较成熟的测试版,与即将发行的正式版相差无几;
RC
:是 Release Candidate 的缩写,意思是发布倒计时,候选版本,处于Gamma阶段,该版本已经完成全部功能并清除大部分的BUG。到了这个阶段只会除BUG,不会对软件做任何大的更改。从Alpha到Beta再到Gamma是改进的先后关系,但RC1、RC2往往是取舍关系。
Stable
:稳定版。在开源软件中,都有stable版,这个就是开源软件的稳定发行版。
范围 >1.2.3-alpha.3
,版本 1.2.3-alpha.7
符合条件,而 3.4.5-alpha.9
却不满足条件。虽然 3.4.5-alpha.9
实际上大于 1.2.3-alpha.3
,但是根据 SemVer
的排序规则,这个版本范围只是接受 1.2.3
的测试版,而不接受其他版本的测试版。当然 3.4.5
满足条件,因为它不是测试版,并且大于 1.2.3-alpha.7
。
这么做是有两个目的,首先测试版会经常更新并且可能包含不适合公开的重大改动,因此被排除在范围之外;再者,虽然用户明确此次使用有风险的测试版本,然而下一版本的测试版被包含进来仍然是不合适的。
版本范围高级用法
在版本范围的高级用法中,与原始的比较器不同,组合不仅仅限于空格和双竖线,它有以下几种方式。
连接符(-)范围 X.Y.Z - A.B.C
下面表示的闭包集合
1.2.3 - 2.3.4
:=>=1.2.3 <=2.3.4
如果范围中的第一个版本号只有一部分,剩下的部分以零填充。
1.2 - 2.3.4
:=>=1.2.0 <=2.3.4
如果范围中的第二个版本号只有一部分,代表范围中包含了这个版本。
1.2.3 - 2.3
:=>=1.2.3 <2.4.0
1.2.3 - 2
:=>=1.2.3 <3.0.0
X范围 1.2.x
1.X
1.2.*
*
X
, x
或者 *
可以用在主版本号、此版本号以及修订版本号的任何一个上面。
*
:=>=0.0.0
(任何版本都满足)1.x
:=>=1.0.0 <2.0.0
(只要满足主版本号即可)1.2.x
:=>=1.2.0 <1.3.0
(需要满足主版本和此版本号)
部分版本号的含义与X范围表示含义一样,所以X或者*都是可以省略的。
""
(空字符串) :=*
:=>=0.0.0
1
:=1.x.x
:=>=1.0.0 <2.0.0
1.2
:=1.2.x
:=>=1.2.0 <1.3.0
波浪线(~)范围 ~1.2.3
~1.2
~1
~1.2.3
:=>=1.2.3 <1.(2+1).0
:=>=1.2.3 <1.3.0
~1.2
:=>=1.2.0 <1.(2+1).0
:=>=1.2.0 <1.3.0
(类似于1.2.x
)~1
:=>=1.0.0 <(1+1).0.0
:=>=1.0.0 <2.0.0
(类似于1.x
)~0.2.3
:=>=0.2.3 <0.(2+1).0
:=>=0.2.3 <0.3.0
~0.2
:=>=0.2.0 <0.(2+1).0
:=>=0.2.0 <0.3.0
(类似于0.2.x
)~0
:=>=0.0.0 <(0+1).0.0
:=>=0.0.0 <1.0.0
(类似于0.x
)~1.2.3-beta.2
:=>=1.2.3-beta.2 <1.3.0
注意版本为1.2.3
且大于等于beta.2
的测试版本在范围内,因此1.2.3-beta.4
满足条件,而1.2.4-beta.2
不满足条件。
补注号(^)范围 ^1.2.3
^0.2.5
^0.0.4
允许在不修改[major, minor, patch]中最左非零数字的更改。换句话说,允许在 1.0.0
及以上版本对次版本号和修订版本号的更新,允许在 0.1.0
以及以上版本对修订版本号更新,版本为 0.0.X
不允许更新。
^1.2.3
:=>=1.2.3 <2.0.0
^0.2.3
:=>=0.2.3 <0.3.0
^0.0.3
:=>=0.0.3 <0.0.4
^1.2.3-beta.2
:=>=1.2.3-beta.2 <2.0.0
^0.0.3-beta
:=>=0.0.3-beta <0.0.4
^1.2.x
:=>=1.2.0 <2.0.0
^0.0.x
:=>=0.0.0 <0.1.0
^0.0
:=>=0.0.0 <0.1.0
^1.x
:=>=1.0.0 <2.0.0
^0.x
:=>=0.0.0 <1.0.0