npm 依赖版本号中 `^` 和 `~` 到底有什么区别?

前言

Axios被爆存在投毒风险,需要尽快规避使用0.30.4和1.14.1版本,这正好是一个活生生的例子,帮助大家理解 ^~ 在实际项目中的影响,在查看 package.json 时,都会看到依赖版本号前面带着 ^~ 符号,比如:

json 复制代码
{
  "dependencies": {
    "axios": "^0.21.0",
    "lodash": "~4.17.20",
    "vue": "^3.2.0"
  }
}

你有没有想过:这两个符号到底代表什么?它们的区别是什么?

今天我们就来彻底搞清楚这个问题。

一、先了解语义化版本(Semver)

在讲 ^~ 之前,我们必须先理解 npm 采用的语义化版本规范(Semantic Versioning)。

一个标准的版本号格式是:

复制代码
Major.Minor.Patch
主版本.次版本.补丁版本

1.2.3 为例:

字段 含义 什么时候变
1(Major) 主版本号 破坏性变更(Breaking Changes)时递增
2(Minor) 次版本号 新增向下兼容的功能时递增
3(Patch) 补丁版本号 修复向下兼容的 Bug时递增

理解了这个,下面就好说了。

二、^(插入符)--- 锁定主版本号

^ 是 npm 的默认行为 。当你执行 npm install axios 时,写入 package.json 的就是 ^ 前缀。

规则:主版本号不变,允许次版本和补丁版本升级。

复制代码
^1.2.3  →  >=1.2.3  且  <2.0.0

示例

写法 允许安装的范围 能装 不能装
^1.2.3 1.2.3 ~ 1.x.x 1.2.41.9.0 2.0.0
^2.0.0 2.0.0 ~ 2.x.x 2.1.02.99.99 3.0.0

⚠️ 特殊情况:主版本为 0

当主版本号是 0 时,^ 的行为会变得更保守

复制代码
^0.21.0  →  >=0.21.0  且  <0.22.0   (只允许补丁更新)
^0.0.3   →  >=0.0.3   且  <0.0.4    (完全锁死)

因为在 semver 规范中,0.x.x 代表不稳定版本,任何次版本的变化都可能是破坏性的,所以 npm 在这里做了特殊处理。

三、~(波浪符)--- 锁定主版本 + 次版本号

规则:主版本号和次版本号都不变,只允许补丁版本升级。

复制代码
~1.2.3  →  >=1.2.3  且  <1.3.0

示例

写法 允许安装的范围 能装 不能装
~1.2.3 1.2.3 ~ 1.2.x 1.2.41.2.99 1.3.0
~4.17.20 4.17.20 ~ 4.17.x 4.17.21 4.18.0

~^ 更保守,它只接受 bug 修复级别的更新。

四、一图对比

复制代码
版本号:Major.Minor.Patch

^1.2.3                          ~1.2.3
  │                               │
  ├── 锁定 Major                  ├── 锁定 Major + Minor
  ├── Minor 可升级 ✅              ├── Minor 不可升级 ❌
  └── Patch 可升级 ✅              └── Patch 可升级 ✅
  
  范围:1.2.3 ~ 1.x.x             范围:1.2.3 ~ 1.2.x
  更宽松,更新更多                   更保守,只收 bug 修复

五、实际场景理解

假设你的 package.json 中有这么一行:

json 复制代码
"axios": "^1.2.3"

当你(或你的同事、CI/CD)执行 npm install 时,npm 会在 ^1.2.3 的范围内选择当前可用的最新版本来安装。

  • 如果此时 axios 最新版是 1.6.0,就会装 1.6.0
  • 如果此时 axios 最新版是 2.0.0,仍然只会装 1.x.x 的最新版

如果改为:

json 复制代码
"axios": "~1.2.3"
  • 如果此时 axios 最新版是 1.2.8,就会装 1.2.8
  • 如果此时 axios 最新版是 1.3.0,仍然只会装 1.2.x 的最新版

六、其他常见写法

除了 ^~,你可能还会见到这些:

写法 含义
1.2.3 精确版本,只装这个版本
* 任意版本(危险,不建议)
>=1.2.3 大于等于 1.2.3 的任意版本
1.2.x 等同于 ~1.2.0
1.x 等同于 ^1.0.0

七、那到底该用哪个?

场景 建议
大多数项目 使用 ^(npm 默认),信任社区遵守 semver
对稳定性要求极高 使用 ~,只接受补丁更新
线上环境要绝对可控 使用精确版本,或依赖 package-lock.json 锁版本

最佳实践 :无论用 ^ 还是 ~,都要把 package-lock.json 提交到 Git 仓库。它会精确锁定每个依赖的实际安装版本,确保团队所有人和 CI/CD 环境安装的版本完全一致。

八、实战案例:Axios 的版本策略建议

情况一:项目使用 0.*.* 版本的 Axios

如果你的 package.json 中写的是 "axios": "^0.21.0",根据前面讲的规则,^0.21.0 的实际范围是 >=0.21.0 且 <0.22.0,行为和 ~ 一致,最多只会更新到 0.21.x。只要你用的不是 0.30.4 这类已知有问题的版本,基本可以放心,不需要额外修改。

情况二:项目使用 1.*.* 版本的 Axios

如果你的 package.json 中写的是 "axios": "^1.2.0",那范围就是 >=1.2.0 且 <2.0.0。这意味着:一旦有团队成员删除了 package-lock.json 后重新 npm install,就有可能安装到 1.14.1 等存在问题的版本。

建议 :对于 1.*.* 版本的 Axios,将版本前缀从 ^ 改为 ~,例如将 "axios": "^1.2.0" 改为 "axios": "~1.2.0",这样即使丢失了 lock 文件,也只会在 1.2.x 范围内更新,大大降低风险。

这个案例也再次说明了一点:package-lock.json 一定要提交到仓库,同时对关键依赖使用 ~ 可以提供额外的安全网。

总结

  • ^:宽松策略,锁主版本号,允许 Minor 和 Patch 更新
  • ~:保守策略,锁主版本 + 次版本号,只允许 Patch 更新
  • 主版本为 0 时,^ 会自动变保守,和 ~ 行为接近
  • 实际项目中 package-lock.json 才是真正的版本锁,^~ 只在 npm install 没有 lock 文件时起作用
相关推荐
子兮曰13 小时前
Bun v1.3.14 深度解析:Image API、HTTP/3、全局虚拟存储与五十项变革
前端·后端·bun
kyriewen14 小时前
今天,百年巨头一次砍了9200人,而一个离职科学家的实话让全网睡不着觉
前端·openai·ai编程
问心无愧051314 小时前
ctf show web 入门42
android·前端·android studio
kyriewen15 小时前
老板逼我上AI,我偷偷在浏览器里跑LLaMA,省下20万API费
前端·react.js·llm
Beginner x_u15 小时前
前端八股整理(手写 02)|数组转树、数组扁平化、随机打乱一个数组
前端·数组·数组转树·数组扁平化
KaMeidebaby15 小时前
卡梅德生物技术快报|禽类成纤维细胞 FISH 实验:鸟类性别染色体基因定位技术实现与数据验证
前端·数据库·其他·百度·新浪微博
天若有情67315 小时前
前端高阶性能优化:跳出传统懒加载与预加载,基于用户行为做轻量预判加载
前端·性能优化
小小小小宇15 小时前
前端转后端:SQL 是什么
前端
张元清16 小时前
React Observer Hooks:7 种监听 DOM 而不写样板代码的方式
前端·javascript·面试
广州华水科技16 小时前
单北斗GNSS变形监测是什么?主要有怎样的应用与优势?
前端