版本号广泛运用于开发的各种场景:NPM 包的版本定义、对 NPM 包的特定版本的依赖指定、git 的 daily 版本号分支……
面对如此多的场景,版本号的命名却存在很大问题。举些例子:
0.0.1
起版本,直到项目不再维护时,版本还停留在 0.0.48
,前两位永远都是 0。 0.0.8
升级到 0.0.9
就引起了整个项目的崩溃。 *
。 根据国际主流的惯例,我们使用「语义化版本( Semantic Versioning )」的命名方式,有时简称 SemVer。
语义化版本号(以下简称「版本号」)的格式是: <major>.<minor>.<patch>
。即使用三位非负整数,以点号 .
连接。
如: 1.4.15
、 6.2.0
。
<major>
即主版本号,俗称大版本升级。改动到主版本号时,标志着 API 发生了巨大变化,包括但不限于新增特性、修改机制、删除功能, 一般不兼容上一个主版本号 。 <minor>
即次版本号,俗称小版本升级。当我们进行常规的新增或修改功能时,改动次版本号,但是 必须是向前兼容的 。这也意味着我们 不能直接删除某个功能 。如若必要,我们可以在 changelog 中标记某项功能为「即将删除(Deprecated)」,然后在下一个大版本中将其彻底删除。 <patch>
即修订号,俗称 bug 修复。顾名思义,如果仅仅为了修复或调整一些小问题,我们就只改动修订号。 所以,当我们明确了每一位的含义和作用后,就不会陷入「每次只改最末位」的尴尬中了。
那如何判断一个修改应该是改动修订号还是次版本号呢?视情况而定。比如对于「修改了 app 图标」这件事来说,如果只是调整了图标的间距位置,那么可以认作问题修复;如果把整个图标换了,配上了不同的标语,那么这应该是一次功能改动。
v
。 0
。错误示例: 01.12.03
。 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>
位版本是不向下兼容的,所以在大版本的控制上,仍然采用人为干预以保证安全。
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 |
略 | 略 | 仅在 特定场景 下有用,默认不使用此字段。 |