config.vendor-dir 是唯一全局生效的路径配置,必须写在项目 composer.json 的 config 字段中(如 "vendor-dir": "third-party"),路径须为相对路径,且需删除旧 vendor 后执行 install/update 才生效;环境变量 composer_vendor_dir 仅在未声明 config.vendor-dir 时起作用。

config.vendor-dir 是唯一全局生效的路径配置
想让所有包都离开 vendor/,只认 config.vendor-dir。它写在项目根目录的 composer.json 里,例如:
{"config": {"vendor-dir": "third-party"}}
这个路径必须是相对路径(third-party ✅,/third-party ❌),且只在 vendor 目录不存在时才真正生效。常见错误是改完配置却没删旧 vendor,结果 composer install 还是原地重建——它不会移动已有文件。
-
composer update和composer require都会尊重这个配置,但前提是vendor已清空 -
composer dump-autoload完全不碰目录结构,也不会更新autoload.php入口路径 - PHP 代码里所有
require 'vendor/autoload.php'必须手动改成新路径,IDE、CI 脚本、bin/下命令调用也得同步改
extra.installer-paths 只对特定 type 的包起作用
这个配置本身不干活,必须配合 composer/installers 插件,且目标包自身 composer.json 中声明了匹配的 type(比如 wordpress-plugin、drupal-module)。普通 library 类型默认不触发。
典型配置示例:
{"extra": {"installer-paths": {"wp-content/plugins/{$name}/": ["type:wordpress-plugin"]}}}
注意:{$name} 会被替换为包名最后一段(acme/my-plugin → my-plugin),路径必须以 / 结尾,且不能以 / 开头。
- 必须先运行
composer require composer/installers,否则整个extra.installer-paths被忽略 - 已安装的包不会被重定位,删掉
vendor/和composer.lock后再跑composer install - 路径值含非法字符(如
\或开头的/)会导致该条规则静默失效
davidbarratt/custom-installer 支持按包名精确控制
当目标包没按生态规范设 type,或者你想按完整包名(如 myorg/ui-kit)定向搬运,davidbarratt/custom-installer 更灵活。它允许你直接匹配包名或 type,无需依赖对方 composer.json 的 type 声明。
配置示例:
{"extra": {"custom-installer": {"myorg/ui-kit": "resources/js/vendor/{$name}/"}}
优先级:包名匹配 > type 匹配。如果同时写了 "library": "public/js/libs/" 和具体包名规则,后者优先生效。
- 插件必须放在
require(不是require-dev)里:composer require davidbarratt/custom-installer:^1.0 - 包被挪走后,
composer dump-autoload不会自动扫描新路径,必须在根composer.json的autoload段手动加映射 - 路径中
{$name}和{$vendor}都可用,但别混用/和\风格
环境变量 COMPOSER_VENDOR_DIR 有优先级陷阱
它只在项目 composer.json 没声明 config.vendor-dir 时才生效。一旦配置文件里写了,环境变量就被无视——不是覆盖,是彻底不读。
临时使用可以:
COMPOSER_VENDOR_DIR="lib" composer install
但别指望它在 CI 或多环境部署中稳定工作。Windows 下若路径以 / 开头(如 /lib),解析可能失败;永久设置要加到 shell 配置文件并重启终端。
- 它不影响
extra.installer-paths或custom-installer的行为 -
-d参数根本不是 Composer 支持的选项,执行无效果也不报错,纯属误导 - 最易被忽略的是 autoload 入口和硬编码路径的同步更新——配置改了,代码和部署脚本没动,一切照旧失败











