composer remove 和 composer update --refresh 是 Composer 仅有的两个原子操作命令:前者一次性校验依赖、删文件、改声明、更新 lock 并重建 autoload;后者丢弃所有本地元数据快照,直连镜像拉最新 packages.json 并重做完整依赖求解,失败即回退,不留下半截状态。

Composer 的 remove 和 update --refresh 是目前仅有的两个明确标注为原子操作的命令——它们不是“尽量做全”,而是失败即回退,不留下半截状态。别被“原子”二字吓住,它本质是 Composer 内部把多步动作打包成一个不可中断的事务。
composer remove 是怎么做到原子卸载的
它不是先删 vendor 再改 composer.json,而是一次性校验依赖图、同步删文件、删声明、更新 lock、重建 autoload 映射。任何一步失败,整个操作就中止,不会出现“vendor 没了但 composer.json 还留着”的错乱状态。
- 自动识别包在
require还是require-dev区域,不用加--dev(除非同名包同时出现在两个区) - 执行
composer remove monolog/monolog后,vendor/monolog/monolog目录、composer.json里对应行、composer.lock中的哈希记录,全部同步清理 - 如果该包被其他已安装包硬依赖(比如你删
guzzlehttp/guzzle,但symfony/http-client依赖它),命令会直接报错并列出引用链,这是保护机制,不是 bug - 想跳过实时重装?加
--no-update:只改composer.json,后续统一跑composer update,适合 CI 分阶段流程
composer update --refresh 强制刷新元数据的真相
composer update --refresh 不是“清缓存 + update”,而是丢弃所有本地 metadata 快照,直连镜像源拉最新 packages.json,再基于它做完整依赖求解。低于 Composer 2.5 的版本不支持该参数,强行使用会报 Unrecognized option "--refresh"。
- 它解决的是“明明镜像源已更新包版本,
composer update却不拉新”的问题——旧 metadata 缓存让你以为没新版本 - 不推荐日常使用,只在确认镜像源已同步新版但本地始终拉不到时触发
- 执行后,
vendor/composer/installed.json和composer.lock都会被重写,但不会动你项目代码或配置 - 如果你用的是私有仓库或自建 Packagist,确保其
packages.json文件确实已更新,否则--refresh也拉不到不存在的版本
为什么手动删 vendor 或改 composer.json 很危险
因为 autoload 映射和 lock 文件哈希是强耦合的。你手删 vendor/foo/bar,但 composer.lock 还记着它的 SHA256,下次 composer install 就会校验失败;你手改 composer.json 删掉一行,但没运行 dump-autoload,Class not found 就立刻报给你看。
-
composer remove自动触发dump-autoload,但如果你用了--classmap-authoritative(Laravel 9+ 默认开启),删包后必须显式执行composer dump-autoload --classmap-authoritative - 某些环境(如 Docker 构建层、CI runner)会缓存 autoloader,此时加
-o强制优化生成:composer dump-autoload -o - 检查
composer.json的autoload.files或autoload.psr-4,是否还残留已删包的路径映射,手动删掉,否则 autoload 仍会尝试加载不存在的文件
原子操作真正难的不是命令怎么敲,而是理解它背后“状态一致性”的约束:Composer 不信任人眼判断,只信任锁文件 + autoload 映射 + vendor 目录三者完全对齐。哪怕你只是多删了一个空格,都可能让后续 install 卡在哈希校验上。











