^
(Caret/插入符号) 和 ~
(Tilde/波浪号) 是用来定义版本范围的,它们遵循 语义化版本(Semantic Versioning, SemVer) 规范。
首先,理解语义化版本 (SemVer)
一个标准的版本号格式是 MAJOR.MINOR.PATCH
,例如 16.8.3
。
MAJOR
(主版本号): 当你做了不兼容的 API 修改(Breaking Changes)。MINOR
(次版本号): 当你做了向下兼容的功能性新增(New Features)。PATCH
(修订号): 当你做了向下兼容的问题修正(Bug Fixes)。
理解了这个,^
和 ~
的区别就很容易了。
~
(Tilde/波浪号):只允许修订号更新
规则: 锁定 MAJOR
和 MINOR
版本,只允许 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
版本,允许 MINOR
和 PATCH
版本的更新。
可以理解为“兼容这个版本”。这是 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 (插入符号) | 更新 MINOR 和 PATCH |
^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
时,都会安装这个被锁定的确切版本,从而保证了环境的一致性。