Composer install 会安装未在 composer.json 中声明的包,是因为 composer.lock 文件仍记录着已删除的依赖;正确清理方式是先确保 composer.json 正确,再运行 composer update(或 --dry-run 预览),而非手动删 vendor。

composer install 时为什么还会装上没在 composer.json 里声明的包?
这通常不是 composer install 的行为,而是项目中残留了旧的 vendor/ 目录、或存在未清理的 composer.lock、又或者你之前执行过 composer require xxx 但没提交修改——导致 composer.lock 里还记着那些包,而 composer.json 已删掉它们。此时 install 会忠实按 lock 文件还原,看起来就像“装了没定义的包”。
如何安全地清理 vendor 中已删除依赖的残留?
别手动删 vendor/ 子目录——容易漏、易误删、破坏 autoload 映射。正确做法是让 Composer 主动同步:先确保 composer.json 是你想要的最终状态(删掉所有不该存在的 require 和 require-dev 条目),再运行:
composer update --with-dependencies
但更稳妥的是:
-
composer update本身就会按composer.json重算依赖树,移除不再需要的包(包括其子依赖) - 加
--dry-run先预览:composer update --dry-run,确认输出里有Removing vendor/package-name这类行 - 若项目用了
platform-check或自定义config.platform,记得同步检查,否则某些包可能因平台约束被意外保留 - 执行后立刻验证
vendor/autoload.php是否仍可正常加载,避免因自动加载规则未刷新导致 class not found
vendor 里还有文件没被自动清理?可能是 autoload 或插件残留
有些包(比如 Laravel 的 service provider、PHP-CS-Fixer 的自定义 fixer、或 Composer 插件)会在安装时写入额外文件到 vendor/ 下非标准位置(如 vendor/bin/ 外的脚本、vendor/composer/ 里的自定义 autoloader 映射),这些不会随 update 自动清除。
这类情况要手动干预:
- 检查
vendor/bin/是否有多余的可执行文件(如旧版phpunit、behat),用ls -la vendor/bin/对比composer.json的bin字段 - 运行
composer dump-autoload -o强制重建自动加载映射,可暴露已删包但 autoload 未更新的问题 - 若用了
composer-plugin-api类插件(如hirak/prestissimo),卸载后需运行composer global remove hirak/prestissimo,否则它可能还在后台影响下载行为
想彻底归零重建 vendor?别跳过 lock 文件校验
直接 rm -rf vendor/ && composer install 看似干净,但前提是 composer.lock 本身已和 composer.json 一致。否则你会复现原来的问题。
推荐流程:
- 先运行
composer validate,确认composer.json语法和约束合法 - 再跑
composer update --lock,只更新 lock 文件(不改 vendor),让它对齐当前 json - 然后
rm -rf vendor/ && composer install - 最后
composer show --installed | wc -l对比前后数量,确认无异常包残留
真正难清理的,往往是那些被其他包间接 require 的“幽灵依赖”——它们没出现在你的 json 里,却因某条依赖链被带进来。这种得靠 composer depends vendor/package 反查源头,而不是盯着 vendor 目录硬删。










