语义化版本(SemVer)
语义化版本(Semantic Versioning,简称 SemVer)是一套版本号命名规范,旨在解决软件版本管理的混乱问题。它定义了一套清晰的规则,让开发者能够通过版本号了解软件的变化程度。
为什么需要语义化版本?
在软件开发中,依赖管理是一个复杂的问题。假设你的项目依赖三个库:A、B 和 C。A 依赖 D v1.0,B 依赖 D v1.5,而 C 依赖 D v2.0。这三个版本的 D 可能存在不兼容的 API 变更,如何选择正确的版本?
语义化版本通过明确的规则解决了这个问题:
- 版本号传达了变更的含义
- 版本号定义了兼容性的边界
- 版本号让依赖解析变得可预测
版本号格式
语义化版本号由三部分组成:主版本号.次版本号.修订号(MAJOR.MINOR.PATCH)
2.1.3
│ │ │
│ │ └── 修订号(Patch)
│ └──── 次版本号(Minor)
└────── 主版本号(Major)
版本号递增规则
主版本号(MAJOR)
当你做了不兼容的 API 变更时递增。这意味着升级主版本可能需要修改代码。例如:
- 移除或重命名公共 API
- 改变函数签名
- 改变默认行为
- 重大架构调整
次版本号(MINOR)
当你做了向后兼容的功能新增时递增。升级次版本通常是安全的。例如:
- 新增公共 API
- 新增可选参数
- 性能优化
- 新增配置选项
修订号(PATCH)
当你做了向后兼容的问题修复时递增。升级修订号应该是完全安全的。例如:
- Bug 修复
- 文档更新
- 代码重构(不影响行为)
版本号递增示例
1.0.0 -> 1.0.1 修复 Bug,递增修订号
1.0.1 -> 1.1.0 新增功能,递增次版本号
1.1.0 -> 2.0.0 破坏性变更,递增主版本号
版本号从 0 开始
版本号 0.x.x 表示初始开发阶段,API 尚不稳定。在这个阶段,任何变更都是允许的,即使是破坏性变更。
0.1.0 -> 0.2.0 可能包含破坏性变更
当 API 稳定后,发布 1.0.0 版本,之后遵循语义化版本规则。
预发布版本
预发布版本用于在正式发布前进行测试。格式为 主版本.次版本.修订号-标识符.数字。
预发布标识符
| 标识符 | 含义 | 示例 |
|---|---|---|
alpha | 内部测试版本 | 1.0.0-alpha.1 |
beta | 公开测试版本 | 1.0.0-beta.2 |
rc | 候选发布版本 | 1.0.0-rc.1 |
预发布版本优先级
预发布版本的优先级低于对应的正式版本:
1.0.0-alpha.1 < 1.0.0-alpha.2 < 1.0.0-beta.1 < 1.0.0-rc.1 < 1.0.0
安装预发布版本
# 安装预发布版本
npm install package@beta
npm install [email protected]
# 安装 next 标签的版本
npm install package@next
版本范围语法
包管理工具使用版本范围语法来指定可接受的版本范围。
基本范围
| 语法 | 含义 | 匹配版本 |
|---|---|---|
1.2.3 | 精确版本 | 仅 1.2.3 |
>1.2.3 | 大于 | 1.2.4 及以上 |
>=1.2.3 | 大于等于 | 1.2.3 及以上 |
<1.2.3 | 小于 | 1.2.2 及以下 |
<=1.2.3 | 小于等于 | 1.2.3 及以下 |
脱字符范围(^)
脱字符(^)允许不改变最左边非零数字的更新。这是 npm 默认使用的范围。
^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:主版本号为 1,允许次版本和修订号更新^0.2.3:主版本号为 0,次版本号为 2,只允许修订号更新^0.0.3:主版本号和次版本号都为 0,相当于精确版本
实际应用
{
"dependencies": {
"express": "^4.18.2"
}
}
这意味着可以安装 4.18.2 到 4.x.x 的任何版本,但不会自动升级到 5.0.0。
波浪号范围(~)
波浪号(~)只允许修订号更新。
~1.2.3 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.3.0
~1 := >=1.0.0 <2.0.0
实际应用
{
"dependencies": {
"express": "~4.18.2"
}
}
这意味着可以安装 4.18.2 到 4.18.x 的任何版本,但不会升级到 4.19.0。
^ 和 ~ 的区别
| 范围 | 允许更新 | 不允许更新 |
|---|---|---|
^1.2.3 | 1.2.4, 1.3.0, 1.4.0 | 2.0.0 |
~1.2.3 | 1.2.4, 1.2.5 | 1.3.0, 2.0.0 |
选择建议:
- 使用
^:信任库的维护者,希望获得新功能和修复 - 使用
~:更保守,只接受 Bug 修复 - 使用精确版本:生产环境关键依赖,需要完全控制
范围组合
连字符范围
1.2.3 - 2.3.4 := >=1.2.3 <=2.3.4
或(||)
^1.0.0 || ^2.0.0
匹配 1.x.x 或 2.x.x 的版本。
与(空格)
>=1.2.3 <1.3.0
同时满足两个条件。
通配符
* := 任意版本
1.x := >=1.0.0 <2.0.0
1.2.x := >=1.2.0 <1.3.0
版本比较规则
版本号按照以下规则比较:
- 从左到右依次比较主版本号、次版本号、修订号
- 数字比较,不是字符串比较
- 预发布版本优先级低于正式版本
1.0.0 < 2.0.0 < 2.1.0 < 2.1.1
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0
预发布版本比较
预发布标识符按点分隔,依次比较:
- 纯数字:按数值比较
- 非纯数字:按 ASCII 字典序比较
- 数字标识符小于非数字标识符
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
版本管理实践
发布流程
首次发布
# 初始化版本
npm version 1.0.0
# 发布
npm publish
Bug 修复
# 修复 Bug 后
npm version patch # 1.0.0 -> 1.0.1
# 发布
npm publish
新增功能
# 新增功能后
npm version minor # 1.0.0 -> 1.1.0
# 发布
npm publish
破坏性变更
# 破坏性变更后
npm version major # 1.0.0 -> 2.0.0
# 发布
npm publish
预发布流程
# 创建 alpha 版本
npm version prerelease --preid=alpha # 1.0.0 -> 1.0.1-alpha.0
# 发布到 alpha 标签
npm publish --tag alpha
# 测试完成后,发布正式版本
npm version patch # 1.0.1-alpha.0 -> 1.0.1
npm publish
版本标签管理
# 查看标签
npm dist-tag ls my-package
# 添加标签
npm dist-tag add [email protected] stable
# 删除标签
npm dist-tag rm my-package old
# 安装特定标签版本
npm install my-package@beta
常用标签:
latest:最新稳定版本(默认)next:下一个主版本beta:测试版本alpha:内部测试版本canary:每日构建版本
依赖更新策略
开发环境
使用宽松的版本范围,获取最新功能和修复:
{
"dependencies": {
"express": "^4.18.2"
}
}
定期更新依赖:
# 检查过期依赖
npm outdated
# 更新到 package.json 允许的范围内
npm update
# 更新到最新版本
npx npm-check-updates -u
npm install
生产环境
使用精确版本或锁文件:
# 使用锁文件安装
npm ci
# 或使用精确版本
npm install --save-exact express
CI/CD 环境
始终使用锁文件:
npm ci
常见问题
1. 版本冲突
当两个依赖要求同一个包的不同版本时:
{
"dependencies": {
"package-a": "1.0.0",
"package-b": "2.0.0"
}
}
解决方案:
- 使用
overrides(npm)或resolutions(yarn)强制版本 - 升级依赖以兼容同一版本
- 寻找替代包
2. 主版本锁定
某些包的主版本之间差异很大:
{
"dependencies": {
"react": "^17.0.0",
"react-dom": "^17.0.0"
}
}
升级主版本前,务必阅读迁移指南。
3. 0.x 版本的不稳定性
0.x.x 版本可能随时发生破坏性变更:
{
"dependencies": {
"new-package": "^0.5.0"
}
}
建议使用精确版本:
{
"dependencies": {
"new-package": "0.5.0"
}
}
4. 预发布版本的意外安装
默认情况下,npm install package 不会安装预发布版本。但如果 package.json 中指定了预发布版本范围:
{
"dependencies": {
"package": "^1.0.0-beta.0"
}
}
可能会安装预发布版本。建议使用精确版本或正式版本范围。
版本号速查表
| 操作 | 命令 | 版本变化 |
|---|---|---|
| 修订号更新 | npm version patch | 1.0.0 → 1.0.1 |
| 次版本更新 | npm version minor | 1.0.0 → 1.1.0 |
| 主版本更新 | npm version major | 1.0.0 → 2.0.0 |
| 预发布版本 | npm version prerelease | 1.0.0 → 1.0.1-0 |
| 带标识预发布 | npm version prerelease --preid=beta | 1.0.0 → 1.0.1-beta.0 |
| 指定版本 | npm version 2.1.0 | → 2.1.0 |
版本范围速查表
| 范围 | 含义 | 匹配示例 |
|---|---|---|
1.2.3 | 精确版本 | 仅 1.2.3 |
^1.2.3 | 兼容版本 | 1.2.3, 1.2.4, 1.3.0 |
~1.2.3 | 近似版本 | 1.2.3, 1.2.4 |
>1.2.3 | 大于 | 1.2.4 及以上 |
>=1.2.3 | 大于等于 | 1.2.3 及以上 |
<1.2.3 | 小于 | 1.2.2 及以下 |
<=1.2.3 | 小于等于 | 1.2.3 及以下 |
1.2.3 - 2.3.4 | 范围 | 1.2.3 到 2.3.4 |
^1.0.0 || ^2.0.0 | 或 | 1.x 或 2.x |
* | 任意版本 | 所有版本 |
1.x | 通配符 | 1.0.0 到 1.x.x |
了解更多
- SemVer 官方规范:https://semver.org
- npm 语义化版本:https://docs.npmjs.com/about-semantic-versioning
- semver npm 包:https://www.npmjs.com/package/semver