composer install 是精确还原而非重新安装,只读取 composer.lock 文件,不解析 composer.json 变更;必须配合 --locked 确保一致性,否则易导致依赖漂移。

直接回答:跳过更新,就用 composer install;想装新包或改依赖,才用 composer update 或 composer require。两者语义完全不同,混用是绝大多数“lock 被改”“dev 包进了生产”的根源。
为什么 composer install 不等于“重新安装”,而是“精确还原”
它只读 composer.lock,完全不看 composer.json 里新加的 require、删掉的包、甚至 PHP 版本约束变更。只要 lock 存在且合法,install 就只是把 vendor 目录“对齐”到 lock 记录的状态。
- 常见错误现象:
composer install后composer.lock时间戳变了、内容有增删 → 其实是lock和composer.json不兼容(比如删了包但 lock 没清理),Composer 在“修复”而非“更新” - CI/CD 必须加
--locked:运行composer install --locked,一旦 lock 缺失或不匹配,立刻失败,绝不自作主张生成新 lock - 性能影响为零:它不解析依赖树、不查版本、不连 Packagist,纯本地文件比对 + 文件复制,快且确定
composer update 和 composer require 的本质区别
update 是重算整个依赖图谱,require 是“添加一个节点并重算局部”。它们都会修改 composer.lock,也都可能破坏环境一致性。
-
composer update:默认更新所有包,哪怕你只改了一行composer.json。加包名如composer update monolog/monolog才只动它和它的子依赖 -
composer require foo/bar:自动写入composer.json并执行一次update,等价于手动改 JSON +composer update foo/bar - 容易踩的坑:在生产部署脚本里误写
composer update --no-dev→ 锁文件被重写,下次install就不是原来那套依赖了
如何真正“跳过某个包”的更新(不是假装跳过)
Composer 没有 --exclude,所谓跳过,只有两种可靠路径:锁死版本,或声明替代。其他方法要么破坏依赖树,要么只在某次生效。
- 最稳方式:在
composer.json中写死版本,例如"laravel/framework": "10.48.5"(不带^或~);之后composer update laravel/framework会提示 “Nothing to install or update” - 想彻底不让它出现:用
"replace": { "monolog/monolog": "*" },但前提是项目真不用它——replace不提供类,只骗过 Composer 的安装逻辑 - 慎用
--without=xxx:它会移除该包及其所有子依赖,如果别的包硬 require 它(比如symfony/consolerequiresymfony/polyfill-php81),命令直接报错退出
安装时跳过 dev 包、跳过扩展、跳过校验的实用参数
这些不是“跳过更新”,而是控制 install 或 update 的行为边界,用错参数反而会让结果不可控。
-
--no-dev:只对composer install有效,且必须配合已存在的composer.lock(否则退化为全量安装);--dev=false是无效参数,会被忽略 -
"config": { "platform": { "ext-sodium": "0.0.0" } }:告诉 Composer “系统没这个扩展”,从而跳过所有 require 它的包(比如某些加密库),适合容器中精简镜像 -
--ignore-platform-reqs:绕过 PHP 版本、扩展检查,仅限调试。线上用了可能导致Fatal error: Uncaught Error: Call to undefined function sodium_crypto_secretbox()
真正的“跳过”不是靠命令行开关临时绕开,而是靠 composer.lock 的存在性、composer.json 中版本号的精确性、以及团队对 install 和 update 场景的严格区分。一旦开始手动删 lock 条目、用脚本 rm vendor、或依赖 --without 保版本,说明约束已经松动,问题只会延迟爆发。










