语义化版本(semver)怎么读:谁更新、预发布与依赖范围全讲清
语义化版本号由主版本、次版本、补丁三段组成。本文讲清怎么判断两个版本谁更新,预发布标签的优先级,以及脱字号波浪号范围怎么圈定可升级区间。
语义化版本(semver)怎么读:谁更新、预发布与依赖范围全讲清
每个写过 package.json 的人都见过 ^18.2.0 这种写法,也都被 1.2.9 和 1.2.10 谁大这种问题绊过一次。语义化版本(Semantic Versioning,简称 semver)给版本号定了一套确定的规则,读懂它,升级依赖时就不用再靠猜。
三段数字各管什么
一个标准的 semver 长这样:主版本.次版本.补丁,比如 2.4.1。三段各有分工,记住这条就够用大半:
- 主版本(major)递增,代表有破坏性改动,旧代码可能跑不起来。比如
1.x.x升到2.0.0,你要去读迁移指南。 - 次版本(minor)递增,代表加了新功能,但向后兼容,老用法照样能用。
1.4.0升到1.5.0属于这类。 - 补丁(patch)递增,代表只修了 bug,不加功能也不破坏接口。
1.5.0到1.5.1就是修了点小毛病。
所以看到 1.9.0 跳到 2.0.0,第一反应应该是警惕;看到 1.9.0 到 1.10.0,放心接,只是多了功能。
比较两个版本,关键在按数值读
这是 semver 最反直觉、也最容易踩的一点。很多人下意识用字符串排序,而字符串是逐字符比的。
举个真实例子:1.2.0 和 1.10.0 谁更新?字符串排序逐字符读补丁那段,看到 2 在 1 之后,会判 1.2.0 更大。但按 semver,次版本要当整数比,2 < 10,所以 1.10.0 才是更新的版本。同理 1.2.10 大于 1.2.9,因为补丁段 10 > 9,而不是按字符 9 排在 1 后面那种读法。
我自己第一次被这条坑,是在一个发布脚本里用了简单的字符串比较,结果灰度版本死活推不上去,排查半天才发现是 1.0.10 被判成比 1.0.9 旧。从那以后,凡是要比版本,我都先丢进 语义化版本比较器 核一遍,它会高亮第一个不同的段,一眼看出问题出在哪。
预发布标签:带横杠的版本更靠前
正式版发布前常有 1.0.0-alpha、1.0.0-beta、1.0.0-rc.1 这类预发布版本。规范规定,带预发布标签的版本优先级低于对应的正式版。也就是说:
1.0.0-alpha < 1.0.0-rc.1 < 1.0.0
预发布内部按点分标识从左到右比:1.0.0-alpha.1 比 1.0.0-alpha.2 旧;纯数字标识排在字母标识之下。还有个容易被忽略的细节,+build.42 这种构建元数据在比较时完全被忽略,1.0.0+build.1 和 1.0.0+build.999 是相等的,它只记录产物怎么来的,不影响谁新谁旧。
脱字号与波浪号:依赖范围怎么圈
依赖文件里很少写死一个版本,更多是写一个允许的范围:
- 脱字号
^1.2.3:允许最左边那个非零数字不变的所有更新版本。1.9.0满足,2.0.0不满足。当主版本是 0 时改锁次版本,^0.2.3允许0.2.9但不允许0.3.0。 - 波浪号
~1.2.3:更紧,只允许补丁递增。1.2.9通过,1.3.0就被挡在外面。
实务里 ^ 是 npm 默认,适合相信上游遵守 semver 的库;~ 适合你只想拿 bug 修复、不想要新功能的场景。判断某个具体版本到底落不落在范围里,可以直接在比较器的范围框输入运算符核对,省得自己脑补边界。
一组版本一次排序
手里有一堆乱序标签时,挨个比太慢。把 1.0.0、1.0.0-alpha、2.0.0、1.0.0-rc.1、1.2.0 这样一组粘进去,按优先级规则排出来就是 1.0.0-alpha、1.0.0-rc.1、1.0.0、1.2.0、2.0.0,预发布的次序和正式版的位置都摆对。整理更新日志、对齐 git 标签发布顺序时特别省事。如果你的版本号格式还不统一,可以先用 JSON 格式化工具 把依赖文件理清,再逐项核版本。
小结
记住三件事:主版本破坏兼容、次版本加功能、补丁修 bug;比较时每段都按整数读,不是按字符;预发布版排在正式版之前,构建元数据不参与比较。范围符号 ^ 和 ~ 分别圈出不同宽窄的可升级区间。把这套规则吃透,再配上一个靠谱的比较器,依赖升级这件事就从靠猜变成有据可查。
Made by Toolora · Updated 2026-06-13