config.vendor-dir 是 composer 唯一全命令一致生效的 vendor 路径配置项,须写在项目根目录 composer.json 中、值为相对路径、且仅在 vendor 不存在时生效;composer_vendor_dir 和 -d 参数不可靠,无法真正改变安装位置。

config.vendor-dir 是唯一真正生效的配置项
想改 vendor 名称,别折腾命令行参数或环境变量——config.vendor-dir 是 Composer 唯一全命令一致生效的路径控制方式。它被 composer install、composer update、composer require 全部识别,且优先级高于 COMPOSER_VENDOR_DIR 和 -d 参数。
但必须满足三个硬前提:
- 配置只能写在项目根目录的
composer.json里,不能放全局或子目录中 - 值必须是相对路径,比如
"libs"✅,不能是/var/www/libs或/libs❌ - 只在
vendor/目录不存在时才起作用;已存在旧目录,Composer 不会迁移,也不会把新包写进新路径
常见翻车:先 composer install 生成了默认 vendor/,再补加 config.vendor-dir —— 此时改完没用,必须手动删掉现有 vendor 目录,再运行 composer install(不是 dump-autoload)。
COMPOSER_VENDOR_DIR 环境变量实际很不可靠
COMPOSER_VENDOR_DIR 看起来方便,但实测中它只在极少数场景下影响安装位置:Composer 2.2+ 中,该变量仅对 dump-autoload 和部分内部路径解析有效,不会改变 install 或 update 时实际创建的目录位置。
你设了 COMPOSER_VENDOR_DIR=lib/ext 再跑 composer install,结果仍是默认 vendor/,且生成的 autoload 文件仍硬编码引用该路径。
更麻烦的是兼容性问题:
- 在 Docker 容器、Git Bash、某些 CI 环境下常被忽略或解析错误
-
composer -d /some/path install只告诉 Composer 去哪找composer.json和composer.lock,完全不改变vendor写入位置——本地跑通但 CI 报Class not found就是因为这个
结论:别依赖它做生产级路径控制,只适合临时调试或脚本兜底。
vendor 目录名本身不能改,改的是路径
很多人误以为 config.vendor-dir: "libs" 是把目录“改名为 libs”,其实不是:它让 Composer 把整个 vendor/ 目录装进 libs/ 子目录下,即最终路径是 libs/vendor,而不是 libs。
如果你真想要 libs 这个名字,得写成 "vendor-dir": "libs",此时依赖会装进 libs/,且 autoload.php 会生成在 libs/autoload.php(注意:不是 libs/vendor/autoload.php)。
但必须同步处理这些环节:
- 代码里所有
require 'vendor/autoload.php'都要改成require 'libs/autoload.php' -
.gitignore要更新,忽略libs/而非vendor/ - CI/CD 脚本、部署工具、IDE 的自动加载索引路径都要跟着改
- 某些插件(如
composer-plugin-api)在初始化阶段就读取vendor结构,路径不对可能静默失败
extra.installer-paths 不是用来改 vendor 根目录的
extra.installer-paths 常被误当成“全局 vendor 替换方案”,但它其实是 composer/installers 插件的能力,只影响声明了明确 type 的包,比如 WordPress 插件、Drupal 模块这类生态型包。
普通 PHP 库(如 monolog/monolog)完全不受影响。使用前还必须:
- 先
composer require composer/installers,否则整个字段被忽略 - 路径值是相对于项目根目录的,不支持以
/开头,例如"wp-content/plugins/{$name}/"✅ - 规则按顺序匹配,只取第一个命中项;已安装的包不会自动迁移,删
vendor后重装才生效
真正要改整个项目的依赖安装位置,就只有一条路:config.vendor-dir + 删旧 vendor + 重装。
最易被忽略的点是 autoload 路径的强耦合——vendor/autoload.php 里大量硬编码路径,包括 PSR-4 映射、classmap、甚至 ClassLoader::setPsr4() 的参数。哪怕只动一个字符,都可能让整条加载链断掉。











