Composer删包后vendor残留是正常行为,因它只更新composer.json和lock文件,物理删除需执行composer install或update触发同步;彻底清理须先移除声明,再运行composer update --with-dependencies或install,最后验证。

composer为什么删了包还留在vendor里
执行 composer remove vendor/package 后,vendor/ 目录下对应文件夹仍存在,不是 bug,是 composer 的默认行为:它只更新 composer.json 和 composer.lock,不主动清理磁盘残留。真正触发物理删除的是 composer install 或 composer update 时的“同步阶段”。
- 手动删
vendor/下某目录 → 下次composer install会把它拉回来(如果还在composer.lock里) - 只改
composer.json但不运行安装命令 →vendor/完全不变 - 运行
composer update --lock不清包,它只重写 lock 文件,不碰 vendor
真正清理冗余依赖的三步操作
所谓“冗余”,指已从 composer.json 移除、但仍在 composer.lock 或 vendor/ 中残留的包。必须按顺序做:
- 确认已从
composer.json的require或require-dev中彻底删除目标包名 - 运行
composer update --with-dependencies(推荐)或composer install—— 前者更安全,它只更新被显式修改的依赖及其子依赖;后者强制按 lock 文件重建 vendor,更快但要求 lock 文件本身已更新 - 验证:检查
vendor/是否无该目录 +composer show不再列出该包
⚠️ 注意:composer update vendor/package 是升级,不是清理;composer remove 是移除声明 + 更新 lock + 清理 vendor(v2.2+ 才默认清理),老版本需补一步 install。
composer-unused 能自动发现未使用包吗
能,但有强限制:composer-unused 是静态扫描工具,它分析代码中 use、new、class_exists 等调用,标记“声明了但没被 PHP 代码直接引用”的包。但它不理解运行时加载、配置驱动、插件机制。
- 适用场景:纯库项目、无 DI 容器、无反射调用、无配置化服务注册
- 常见误报:Laravel 的 service provider、Symfony 的 bundle、Doctrine 的 type 扩展、任何通过字符串类名实例化的组件
- 运行前先
composer install --no-dev,否则 dev-only 包会被误判为冗余 - 命令示例:
composer unused --no-interaction --exclude=tests
清理后 vendor 还很大?可能是 autoload 陷阱
有些包体积大,但你只用了其中 1 个类,却因 autoload 配置被整包加载。比如 monolog/monolog 的 psr-4 映射覆盖整个 src/,哪怕只 use Monolog\Logger,Composer 也会把所有 handler、formatter 全加载进 autoloader map。
- 检查
vendor/composer/autoload_psr4.php,看目标包是否映射了过宽路径 - 无法改上游配置时,可用
composer config autoloader-suffix MyProject强制生成独立 autoloader,减少冲突 - 极端情况可手动删 vendor 中不用的子目录(如
vendor/foo/bar/src/UnusedModule/),但下次update会恢复 —— 这不是持久方案,只是临时减体积
真正的轻量依赖,得靠选包时看它的 autoload 粒度,而不是清理时硬删。










