答案是使用davidbarratt/custom-installer插件并配置extra.custom-installer规则,按包type或完整名称将其安装到public/js/libs/、web/modules/contrib/等自定义路径,同时同步更新autoload映射。

Composer 默认把所有包装进 vendor/,但你完全可以让它把某些包放到 public/js/libs/、app/modules/custom/ 甚至任意目录——关键不是“能不能”,而是“怎么配才不翻车”。
为什么默认 vendor/ 不够用?
很多真实项目结构根本不是扁平的:Drupal 要求模块在 web/modules/,前端库要放在 public/js/,自定义 PHP 模块得进 app/extensions/。硬塞进 vendor/ 会导致路径错乱、Web 服务器无法访问、部署脚本失效,甚至让自动加载器找不到类。
手动复制、软链接、post-install-cmd 脚本这些补丁式方案,要么不可靠,要么跨平台失败,要么在 CI/CD 中彻底失控。
用 davidbarratt/custom-installer 精确控制安装路径
这个插件是目前最轻量、最稳定、社区验证过的路径重定向方案。它不改 Composer 核心逻辑,只在安装阶段介入,按配置把包“搬”到指定位置。
- 先加依赖:
composer require davidbarratt/custom-installer:^1.0(务必放根composer.json的require里,不能放require-dev) - 在
composer.json的extra下加规则,例如:
{
"extra": {
"custom-installer": {
"library": "public/js/libs/",
"drupal-module": "web/modules/contrib/",
"my-company/package-name": "app/extensions/my-package/"
}
}
}
注意:library 是包的 type 字段值,不是随便写的;my-company/package-name 是完整包名,优先级高于 type 匹配。
如果目标包没声明 type,你得先 fork 它,在它的 composer.json 里加上 "type": "library" 再发布,否则规则不生效。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
config.vendor-dir 只能全局挪动,别误用
有人看到 "config": { "vendor-dir": "custom-vendor" } 就以为能分包管理——其实这只是把整个 vendor/ 目录换个名字,所有包还是挤在一起,解决不了“某个包单独放哪”的问题。
这个配置唯一适合的场景是:你有强制规范要求所有 PHP 依赖必须进 lib/ 或 deps/,且接受全部包仍统一存放。
它和 custom-installer 完全不冲突,可以共存,但作用域完全不同:前者管“容器位置”,后者管“包落点”。
容易被忽略的坑:autoload 和路径必须同步更新
包被装到新路径后,composer dump-autoload 默认不会扫描那里——除非你显式告诉它。
- 如果新路径下有 PHP 类,必须在根
composer.json的autoload或autoload-dev里加对应映射,例如:"App\Extension\": "app/extensions/my-package/src/" - 如果只是前端 JS/CSS,就不用管 autoload,但 Web 服务器配置(如 Nginx root)必须指向正确路径,否则 404
- 运行
composer install前,确保composer.lock已提交;否则团队成员拉代码后,custom-installer规则不会触发,包仍会落到vendor/
最常出问题的是:规则写了,路径也改了,但忘了更新 autoload 映射,结果类加载失败,报 Class not found——这时候别急着删 lock 文件,先检查 autoload 配置是否覆盖了新位置。

















