前言
Axios被爆存在投毒风险,需要尽快规避使用0.30.4和1.14.1版本,这正好是一个活生生的例子,帮助大家理解 ^ 和 ~ 在实际项目中的影响,在查看 package.json 时,都会看到依赖版本号前面带着 ^ 或 ~ 符号,比如:
{
"dependencies": {
"axios": "^0.21.0",
"lodash": "~4.17.20",
"vue": "^3.2.0"
}
}
你有没有想过:这两个符号到底代表什么?它们的区别是什么?
今天我们就来彻底搞清楚这个问题。
一、先了解语义化版本
在讲 ^ 和 ~ 之前,我们必须先理解 npm 采用的语义化版本规范。
一个标准的版本号格式是:
Major.Minor.Patch
主版本.次版本.补丁版本
以 1.2.3 为例:
| 字段 | 含义 | 什么时候变 |
|---|---|---|
1(Major) | 主版本号 | 有破坏性变更时递增 |
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.4、1.9.0 | 2.0.0 |
^2.0.0 | 2.0.0 ~ 2.x.x | 2.1.0、2.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.4、1.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 中有这么一行:
"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的最新版
如果改为:
"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 文件时起作用