Composer remove后vendor目录残留是设计行为,非bug;它仅更新composer.json和composer.lock,物理删除需执行composer install或update触发同步。

composer remove 后 vendor 还在,不是 bug 是设计
执行 composer remove vendor/package 后,vendor/ 下对应目录仍存在,是 Composer 2.2+ 的默认行为——它只更新 composer.json 和 composer.lock,不主动删文件。真正触发物理删除的,是后续的 composer install 或 composer update 同步阶段。
常见误操作:
- 手动删了
composer.json里的require行,但没运行任何安装命令 →vendor/完全不变 - 运行
composer update --lock→ 只重写 lock 文件,不碰vendor/ - 用
composer update vendor/package→ 这是升级,不是卸载
真正清理冗余包必须走三步闭环
所谓“冗余”,指已从 composer.json 移除、却还卡在 composer.lock 或 vendor/ 里的包。安全清理不能跳步:
- 确认该包名已从
composer.json的require或require-dev中彻底删除(注意大小写、斜杠方向) - 运行
composer update --with-dependencies(推荐)或composer install;前者只更新被修改依赖及其子依赖,更可控 - 验证:检查
vendor/是否无该目录 +composer show vendor/package返回空
⚠️ 注意:composer remove 在老版本(vendor/,必须补 install;新版本虽默认清理,但若该包被其他依赖间接引用,仍会保留。
想自动删 tests/docs/example?靠 post-install-cmd 脚本
Composer 本身不提供 “安装后自动删无用目录” 的开关,但可通过 post-install-cmd 实现。在 composer.json 中加:
"scripts": {
"post-install-cmd": [
"find vendor/*/* -type d \( -name 'tests' -o -name 'Tests' -o -name 'test' -o -name 'docs' -o -name 'doc' -o -name 'examples' -o -name 'example' -o -name '.github' \) -exec rm -rf {} + || true",
"find vendor/* -type d -name '.git' -exec rm -rf {} + || true"
]
}
说明:
- 适用于 Linux/macOS;Windows 需改用 PowerShell 或 Git Bash 执行等效逻辑
-
|| true防止某目录不存在导致整个安装中断 - 该脚本只在
composer install或composer update后触发,不影响composer require
别信 composer-unused 自动标“无用”,它漏得太多
composer-unused 是静态扫描工具,只能识别代码中显式出现的 use、new、class_exists() 等调用。以下情况它完全看不见:
- Laravel 的
ServiceProvider、Symfony 的 bundle 注册(配置驱动,无 new) - Doctrine type 扩展、Monolog handler(通过字符串类名注册)
- 反射调用:
new ReflectionClass('Some\Class') - 测试里用到的包(
--no-dev安装后才暴露问题)
真要验证一个包是否还能删,最可靠的是:grep -r 'Vendor\Package' . --include="*.php" + 检查所有 config 文件中的 providers/aliases/facade 配置项。
自动清理最易被忽略的点:你删了包,但它的命名空间可能还留在 vendor/composer/autoload_psr4.php 里——因为 dump-autoload 没强制重建。每次 remove 后,务必跟一句 composer dump-autoload -o,否则类仍能加载,掩盖真实问题。











