关于package-json中的版本号

^ (Caret/插入符号) 和 ~ (Tilde/波浪号) 是用来定义版本范围的,它们遵循 语义化版本(Semantic Versioning, SemVer) 规范。

首先,理解语义化版本 (SemVer)

一个标准的版本号格式是 MAJOR.MINOR.PATCH,例如 16.8.3

  • MAJOR (主版本号): 当你做了不兼容的 API 修改(Breaking Changes)。
  • MINOR (次版本号): 当你做了向下兼容的功能性新增(New Features)。
  • PATCH (修订号): 当你做了向下兼容的问题修正(Bug Fixes)。

理解了这个,^~ 的区别就很容易了。

~ (Tilde/波浪号):只允许修订号更新

规则: 锁定 MAJORMINOR 版本,只允许 PATCH 版本的更新。

可以理解为“约等于这个版本”。它认为,只有 bug 修复是足够安全的,可以自动更新。

示例:~1.2.3

  • 它会匹配所有 1.2.x 系列的最新版本。
  • 允许安装1.2.3, 1.2.4, 1.2.99
  • 禁止安装1.3.0 (次版本号变了), 2.0.0 (主版本号变了)

技术上的等价范围: >=1.2.3 <1.3.0

一句话总结:我只想要 bug 修复,不要新功能。

^ (Caret/插入符号):允许次版本和修订号更新

规则: 锁定 MAJOR 版本,允许 MINORPATCH 版本的更新。

可以理解为“兼容这个版本”。这是 npm install <package> 时的 默认行为。它基于一个信任:只要主版本号不变,就不会有破坏性更新,所以新的功能和 bug 修复都应该是安全的。

示例:^1.2.3

  • 它会匹配所有 1.x.x 系列的最新版本。
  • 允许安装1.2.3, 1.2.4, 1.3.0, 1.99.99
  • 禁止安装2.0.0 (主版本号变了)

技术上的等价范围: >=1.2.3 <2.0.0

一句话总结:我想要 bug 修复和新的、向后兼容的功能,但不要任何可能破坏我代码的更新。

一个重要的例外:0.x 版本

在 SemVer 规范中,0.x.y 版本被认为是 不稳定 的早期开发阶段。在这个阶段,即使是 MINOR 版本的变化也可能包含破坏性更新

因此,对于 0.x 的版本,^ 的行为会变得和 ~ 一样保守。

示例:^0.2.3

  • 它的行为会像 ~0.2.3 一样。
  • 允许安装0.2.3, 0.2.4, 0.2.9
  • 禁止安装0.3.0, 1.0.0
  • 技术上的等价范围: >=0.2.3 <0.3.0

这是为了保护开发者,避免在不稳定的早期依赖中引入意想不到的破坏性变更。

对比

符号 名称 规则 示例 1.4.2 允许的更新 适用场景
~ Tilde (波浪号) 只更新 PATCH ~1.4.2 1.4.3, 1.4.9 非常保守,只接受 bug 修复。适用于对稳定性要求极高的关键项目。
^ Caret (插入符号) 更新 MINORPATCH ^1.4.2 1.4.3, 1.5.0, 1.9.9 npm 默认。在信任依赖包遵循 SemVer 的前提下,平衡了新功能和稳定性。
(无符号) Exact (精确版本) 不更新 1.4.2 只有 1.4.2 终极稳定,锁定版本。但会错过重要的安全补丁。通常由 package-lock.json 管理。

结论与实践建议

  • ^ 是最常用、也是 npm 的默认设置。 它体现了一种社区的信任,即包的维护者会遵守 SemVer 规则。对于大多数项目来说,这是最好的选择,因为它能让你在不破坏代码的情况下自动获得 bug 修复和新功能。
  • ~ 更加保守。 如果你正在处理一个非常敏感的生产系统,即使是一个新的、非破坏性的功能也可能带来未知的风险(比如性能变化或微小的行为差异),那么使用 ~ 会更安全。
  • 最终的控制权在 package-lock.json 无论你在 package.json 里用的是 ^ 还是 ~,当你运行 npm install 时,npm 会根据这些规则计算出要安装的具体版本,并把这个版本锁定package-lock.json 文件中。之后,团队中的任何人以及 CI/CD 服务器运行 npm ci 时,都会安装这个被锁定的确切版本,从而保证了环境的一致性。