~1.2.3 允许安装 1.2.x 范围内所有版本,等价于 >=1.2.3 <1.3.0;而 ^1.2.3、^1.2.0、^1.0.0、^0.8.2、^0.8.0 分别对应各自主版本兼容范围。
~1.2.3 到底允许装哪些版本?
它只放行
范围内的所有版本,等价于
只改了一个文档 typo,也不会被接受。
→ 允许
、
、
,拒绝
和
(省略 patch)→ 自动补为
,即
→ 补全为
,即
重合,但语义不同:一个是“锁次版本锚点”,一个是“守主版本兼容”
什么时候必须用 ~,不能靠 ^?
当你依赖某个包的
次版本级行为细节
,比如返回结构、钩子调用时机、配置项默认值在
和
之间有隐式差异,而测试只覆盖了
时,
可能悄悄升到
,直接导致线上报错。
金融/支付类模块上线前要求人工验证每次 minor 升级 → 用
锁死分支
想跳过已知 bug 的小版本(如
内存泄漏),又不想完全锁死失去后续 patch →
比
更可控
CI 构建环境要求可重复、可审计 →
提供更窄的升级窗口,降低非预期变更概率
0.x 版本下 ~ 和 ^ 的陷阱在哪?
两者都极度保守,但原因不同:
不是“宽松”,而是语义化版本强制降级为仅 patch 升级(
同样卡死在
,但它的逻辑是“锚定最左侧非零段”,这里就是
所在的次版本位。
≡
≡
但如果你写成
,它等价于
,仍卡在
;而
同样是
真正危险的是误以为
能升到
—— 它不能,连
都要确认是否已发布且满足约束
怎么验证当前生效的约束范围?
别只看
里写的符号,要看真实安装结果和更新行为。
查已装版本:
,显示的是
下实际加载的版本
预览下次 update 会拉什么:
,它按当前
+
综合计算
快速定位谁阻止你升级:
,能立刻告诉你哪个依赖硬性要求了更低版本
必须提交进 Git —— 否则
会重新解析约束,结果不可控
最常被忽略的一点:约束符号只在
或首次
时起作用;一旦版本写进
,符号就“失能”了——所以 lock 文件不是可选,是契约本身。
1.2.x>=1.2.3 。不是“差不多”,是硬性截断:哪怕 1.3.0~1.2.31.2.41.2.101.2.991.3.02.0.0~1.2~1.2.0>=1.2.0 ~1~1.0.0>=1.0.0 ,此时行为和 ^1.0.01.2.x1.3.x1.2.x^1.2.31.8.0~1.2.3monolog/monolog 2.7.5~2.7.4^2.7.4~^0.8.2>=0.8.2 );而 ~0.8.20.8.x8^0.8.2~0.8.2>=0.8.2 —— 表面写法不同,实际效果一致~0.8~0.8.00.8.x^0.8>=0.8.0 ^0.8.20.9.00.8.3composer.jsoncomposer show monolog/monolog -ivendor/composer update --dry-run monolog/monologcomposer.jsoncomposer.lockcomposer prohibits monolog/monolog:2.8.0composer.lockcomposer installcomposer updateinstallcomposer.lock